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

Posted LQS_Android

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在PopWindow弹窗中使用ViewPager复杂布局+圆形指示器相关的知识,希望对你有一定的参考价值。

其实这个篇文章是将上一篇在Activity中的逻辑改到PopupWindow中进行实现的,将简单的导航大图改成不太复杂的xml布局文件,利用弹窗的透明度进行展示罢了。

预览图如下:

在MainActivity.class中点击进行弹窗展现,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.class代码如下:

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) {
        //弹窗展示
        GiftsPopupWindow giftsPopupWindow = new GiftsPopupWindow(this);
        giftsPopupWindow.showAtLocation(v, Gravity.CENTER,0,0);
    }
}

弹窗类GiftsPopupWindow.class代码如下:

package com.xw.guideviewpagerdemo.view;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.RelativeLayout;

import androidx.viewpager.widget.ViewPager;

import com.xw.guideviewpagerdemo.R;
import com.xw.guideviewpagerdemo.adapter.GuideViewPagerAdapter;

import java.util.ArrayList;

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

    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;
    private Context mContext;

    private final View mPopGiftView;
    public GiftsPopupWindow(Context context) {
        //设置它宽高
        super(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
        mContext=context;
        //这里要注意:设置setOutsideTouchable之前,先要设置:setBackgroundDrawable,
        //否则点击外部无法关闭pop.
        setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        setOutsideTouchable(true);
        setTouchable(true);
        setFocusable(true);
        //载进来View
        mPopGiftView = LayoutInflater.from(context).inflate(R.layout.activity_guide_page, null);
        //设置内容
        setContentView(mPopGiftView);
        //设置窗口进入和退出的动画
        setAnimationStyle(R.style.pop_animation);
        initView();
    }

    private void initView() {
        mViewpager = mPopGiftView.findViewById(R.id.guide_viewpager);
        //指示器:底部灰色小圆点的布局容器
        mIndicatorContainer = mPopGiftView.findViewById(R.id.indicator_container);
        //使用相对布局,小灰点上层小红点
        mRedDot = mPopGiftView.findViewById(R.id.red_dot);
        //初始化引导页大图片
        initGuideImage();
        //初始化指示器的小灰点
        initIndicatorGrayDot();
        GuideViewPagerAdapter guideViewPagerAdapter = new GuideViewPagerAdapter(mContext,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(mContext);
            imageView.setBackgroundResource(mImageIds[i]);
            mImageViewList.add(imageView);
        }
    }
    //初始化指示器底部默认的灰色圆点:未滑到、选中该页
    public void initIndicatorGrayDot(){
        for (int i=0;i<mImageIds.length;i++){
            ImageView pointView = new ImageView(mContext);
            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);
        }
    }
}

上面的代码注释很清楚,除了加载xml布局方式的不同其他代码都是从这里拷贝过来的,理解的过程、流程可参考这篇文章的注释:(这是我前一天写的)

https://blog.csdn.net/luqingshuai_eloong/article/details/118490292

其中加载的弹窗的布局如下:activity_page_guide.xml:(就是前一篇GuidePageActivity的布局,这里换在PopupWindow中进行加载展示实现)

<?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="90dp"
        >
        <!-- 用于动态添加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的适配器GuideViewPagerAdapter.class代码如下,在这里加载的是一个xml布局,而不是上一篇中单独的图片了:

package com.xw.guideviewpagerdemo.adapter;

import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;

import com.xw.guideviewpagerdemo.GiftDetailActivity;
import com.xw.guideviewpagerdemo.R;

import java.util.ArrayList;

import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;

/**
 * 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);
        ImageView closeBtn =view.findViewById(R.id.friend_close_btn);
        closeBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (Activity.class.isInstance(mContext)) {
                    // 转化为activity,然后finish就行了
                    Activity activity = (Activity) mContext;
                    activity.finish();
                }
            }
        });
        giftsImage.setImageResource(R.drawable.friend_no_birthday_icon);
        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);
    }
}

加载的这个布局资源friend_gift_item.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#80000000"
   >
   <ImageView
       android:id="@+id/friend_close_btn"
       android:layout_width="35dp"
       android:layout_height="35dp"
       android:layout_marginTop="130dp"
       android:layout_gravity="right"
       android:layout_marginRight="45dp"
       android:src="@drawable/friend_gift_close"
       />
   <ImageView
       android:id="@+id/gift_img"
       android:layout_width="match_parent"
       android:layout_height="300dp"
       android:gravity="center"
       android:src="@drawable/friend_no_birthday_icon"
      />
   <TextView
       android:id="@+id/gift_from"
       android:layout_width="match_parent"
       android:layout_height="35dp"
       android:gravity="center"
       android:text="来自Lisa的礼物...."
       android:textColor="@color/white"
       />

   <TextView
       android:id="@+id/btn_detail"
       android:layout_width="match_parent"
       android:layout_height="35dp"
       android:layout_marginLeft="50dp"
       android:layout_marginRight="50dp"
       android:gravity="center"
       android:text="查看详情"
       android:background="@drawable/bg_home_number"
       android:textColor="@color/white"
       android:layout_marginTop="35dp"
      />

</LinearLayout>

预览图是这样的:

跳转到GiftDetailActivity.class详情页只包含一个TextView的展示,这里不再贴出。

一些动画资源如下:

 加载的动画资源:

setAnimationStyle(R.style.pop_animation);

 styles.xml:

<resources>
    <style name="pop_animation" parent="android:Animation" >
        <item name="android:windowEnterAnimation">@anim/pop_in</item>
        <item name="android:windowExitAnimation">@anim/pop_out</item>
    </style>
</resources>

在anim文件夹下动画资源:pop_in.xml :

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">

    <translate
        android:fromYDelta="100%"
        android:duration="300"
        android:toYDelta="0"/>


    <alpha android:fromAlpha="0.8"
        android:duration="300"
        android:toAlpha="1.0"/>

</set>

pop_out.xml:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <translate
        android:fromYDelta="0"
        android:duration="300"
        android:toYDelta="100%"/>

    <alpha android:fromAlpha="1.0"
        android:duration="300"
        android:toAlpha="0.8"/>
</set>

至此。完结!

岁月鎏金,谨以此文,铭记今天的青春!!

以上是关于在PopWindow弹窗中使用ViewPager复杂布局+圆形指示器的主要内容,如果未能解决你的问题,请参考以下文章

Android接口回调总结,以及运用到弹窗PopWindow的Demo实现

关于laui 弹窗使用弹窗中jsp点击按钮不能用小问题

弹窗中修改select默认值遇到的问题

在PopupWindow弹窗中使用Glide播放GIF完毕后关闭弹窗并跳转Activity

[Layui]弹窗中再弹窗让新弹窗再父页面下弹出

[Layui]弹窗中再弹窗让新弹窗再父页面下弹出