ViewPager+圆形指示器实现左右滑动的导航欢迎页

Posted LQS_Android

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ViewPager+圆形指示器实现左右滑动的导航欢迎页相关的知识,希望对你有一定的参考价值。

预览效果如下: 

代码实现如下:

第一步:MainActivity中添加一个跳转导航页的按钮,布局文件activity_main.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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:gravity="center"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/jump_guide_activity"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="新手引导页" />

</LinearLayout>

MainActivity.java代码:

package com.xw.guidepagedemo;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button mJumpGuidePage;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initview();
        initListener();
    }
    private void initview() {
        //跳转导航页的按钮
        mJumpGuidePage = findViewById(R.id.jump_guide_activity);
    }

    private void initListener() {
        //跳转导航页的按钮的点击事件
        mJumpGuidePage.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Intent intent = new Intent();
        intent.setClass(this,GuidePageActivity.class);
        startActivity(intent);
    }
}

 第二步:创建导航页的Activity:GuidePageActivity.class和布局文件activity_page_guide.xml:

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    tools:context=".GuidePageActivity">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/guide_viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="50dp"
        >
        <!-- 用于动态添加shape圆形灰色小圆点的指示器的布局 -->
        <LinearLayout
            android:id="@+id/indicator_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
        </LinearLayout>
        <!-- 用于遮盖在灰色小圆点上面的红色小圆点 -->
        <ImageView
            android:id="@+id/red_dot"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/shape_circle_selected"
            />
    </RelativeLayout>

</RelativeLayout>

上面的布局文件中,通过相对布局的进行组件的布局,在ViewPager组件上添加一个相对布局的指示器容器,指示器容器中包含一个用于动态添加shape圆形灰色小圆点的线性布局和一个ImageView组件,这个ImageView组件默认和第一个小灰点重合。

其中小灰点、小红点是通过shape类型的资源定义的:

小灰点:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <size
        android:width="12dp"
        android:height="12dp"/>
    <solid android:color="#5000"/>
</shape>

小红点:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <size
        android:width="12dp"
        android:height="12dp"/>
    <solid android:color="#f00"/>
</shape>

核心代码在GuidePageActivity.class中:

package com.xw.guidepagedemo;

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.ViewPager;

import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;

import com.xw.guidepagedemo.adapter.GuideViewPagerAdapter;

import java.util.ArrayList;

public class GuidePageActivity extends AppCompatActivity {

    private ViewPager mViewpager;
    private int[] mImageIds={R.drawable.ic_img1,R.drawable.ic_img2,R.drawable.ic_img3,R.drawable.ic_img4};
    private ArrayList<ImageView> mImageViewList;
    private LinearLayout mIndicatorContainer;
    private ImageView mRedDot;
    private int mPointWidth;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getSupportActionBar().hide();
        setContentView(R.layout.activity_guide_page);
        initView();
    }

    private void initView() {
        mViewpager = findViewById(R.id.guide_viewpager);
        //指示器:底部灰色小圆点的布局容器
        mIndicatorContainer = findViewById(R.id.indicator_container);
        //使用相对布局,小灰点上层小红点
        mRedDot = findViewById(R.id.red_dot);
        //初始化引导页大图片
        initGuideImage();
        //初始化指示器的小灰点
        initIndicatorGrayDot();
        GuideViewPagerAdapter guideViewPagerAdapter = new GuideViewPagerAdapter(getApplicationContext(),mImageViewList);
        mViewpager.setAdapter(guideViewPagerAdapter);

        /**
         * 这里需要计算两个灰色小圆点的距离,使得小红点随着ViewPager页面滑动时也跟着移动位置
         * 小红点移动距离计算公式:页面移动的百分比positionOffset * 两个灰色小圆点的间距length
         */
        mViewpager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            /**
             * 页面滑动时的回调
             * @param position  当前页面位置索引
             * @param positionOffset :页面滑动偏移的百分比:0.0-1.0,特点:向右滑到新的一页时,滑动偏移的百分比到达1后,置零,从零重新开始计算新的一页的滑动偏移的百分比
             * @param positionOffsetPixels 页面滑动偏移的像素
             */
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
              Log.d("TAG","当前位置:"+position+";当前页面滑动的百分比:"+positionOffset);
                /**
                 * 计算小红点移动时的左边距:通过视图树监听中计算出来的两个小灰点之间的间距✖页面滑动偏移的百分比=mPointWidth*positionOffset
                 * 由于页面滑动百分比的计算特点可知,当小红点滑到新的一页后继续滑动时,当前左边距需要累加前position个页的mPointWidth宽度
                 */
               int leftMargin = (int) (mPointWidth*positionOffset+position*mPointWidth);

               //获取小红点的左边距属性,修改赋值
                RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)mRedDot.getLayoutParams();
                params.leftMargin = leftMargin;
                //将布局参数设置给视图(小红点)
                mRedDot.setLayoutParams(params);
            }

            /**
             * 页面选中时的回调
             * @param position
             */
            @Override
            public void onPageSelected(int position) {

            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

        /**
         * 通过视图树监听计算小灰点之间的间距:视图View在绘制方法layout()执行结束之后,布局的位置、边距大小、尺寸才能确定
         */
        mRedDot.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                //第二个小圆点距离左侧屏幕的距离
                int secondGrayDotLeft = mIndicatorContainer.getChildAt(1).getLeft();
                //第一个小圆点距离左侧屏幕的距离
                int firstGrayDotLeft = mIndicatorContainer.getChildAt(0).getLeft();
                //两个小圆点之间的间距
                mPointWidth = secondGrayDotLeft-firstGrayDotLeft;
                Log.d("TAG","两个小圆点之间的间距:"+ mPointWidth);
                //为了避免这个视图树观察者频繁监听 所以最后需要移除一下监听对象
                mRedDot.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        });
    }

    public void initGuideImage(){
        mImageViewList = new ArrayList<>();
        for (int i=0;i<mImageIds.length;i++){
            ImageView imageView = new ImageView(this);
            imageView.setBackgroundResource(mImageIds[i]);
            mImageViewList.add(imageView);
        }
    }
    //初始化指示器底部默认的灰色圆点:未滑到、选中该页
    public void initIndicatorGrayDot(){
        for (int i=0;i<mImageIds.length;i++){
            ImageView pointView = new ImageView(this);
            pointView.setBackgroundResource(R.drawable.shape_circle_default);
            //设置布局参数
            LinearLayout.LayoutParams params =new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            if (i>0){
                //从第二个圆点开始设置左边距为10
                params.leftMargin=10;
            }
            pointView.setLayoutParams(params);
            mIndicatorContainer.addView(pointView);
        }
    }

}

上面的代码绘制步骤是:

①先绘制4张大的导航图

②再根据导航图的数量绘制对应几个指示器的小灰点(四张图就绘制4个小灰点)

③再绘制小红点,小红点默认覆盖在第一个小灰点上

④通过视图树监听layout()布局完成,计算两个小灰点之间的距离

⑤根据ViewPager的滑动监听器获取到页面的滑动百分比,结合第④步计算小红点随页面滑动的距离

⑥获取小红点左侧边距的属性,对小红点左边距动态赋值,实现小红点随页面滑动二移动。

一些公式都在注释中,仔细琢磨可以看懂,很简单。

其中,ViewPager的适配器GuideViewPagerAdapter.class如下:

package com.xw.guidepagedemo.adapter;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;
import java.util.ArrayList;

/**
 * Copyright (c)2021 网络科技有限公司
 *
 * @author: LQS
 * @date: 2021/7/2
 * @description:
 */
public class GuideViewPagerAdapter extends PagerAdapter {

    private ArrayList<ImageView> mImageViewList;
    private Context mContext;

    public GuideViewPagerAdapter(Context context, ArrayList<ImageView> imageList) {
        mImageViewList = imageList;
        mContext = context;
    }

    @Override
    public int getCount() {
        return mImageViewList.size();
    }

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

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        //加载引导页大图片
        container.addView(mImageViewList.get(position));
        return mImageViewList.get(position);
//        //加载指定的布局文件
//        View view = View.inflate(mContext, R.layout.friend_gift_item, null);
//        ImageView giftsImage =view.findViewById(R.id.gift_img);
//        giftsImage.setImageResource(mImageIDs[position]);
//        TextView detailBtn = view.findViewById(R.id.btn_detail);
//        detailBtn.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View v) {
//                Intent intent = new Intent();
//                intent.setFlags(FLAG_ACTIVITY_NEW_TASK);
//                intent.setClass(mContext, GiftDetailActivity.class);
//                mContext.startActivity(intent);
//            }
//        });
//        container.addView(view);
//        return view;
    }

    /**
     * 该方法一定要重写,从container中移除上一个ImageView对象,否则报错
     *
     * @param container
     * @param position
     * @param object
     */
    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        container.removeView((View) object);
    }
}

效果图见上,到此完结!

遇见即是缘分,欢迎评论、点赞、关注!

以上是关于ViewPager+圆形指示器实现左右滑动的导航欢迎页的主要内容,如果未能解决你的问题,请参考以下文章

Kotlin 实现可点击可滑动顶部导航栏(AppBarLayout+TabLayout)和左右切换可滑动页面(ViewPager)的功能

ViewPager--左右可滑动的

在PopWindow弹窗中使用ViewPager复杂布局+圆形指示器

Android- fragment结合ViewPager实现左右滑动

自适应 Tab 宽度可以滑动文字逐渐变色的 TabLayout(仿今日头条顶部导航)

ViewPager 滑动不适用于 RecyclerView