在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实现