效果图
每日提醒弹窗
前言
为了增强用户的体验,所以增加了这个每日弹窗,每日弹窗顾名思义,每天弹出一次,就不再弹窗,当然如果用户觉得烦的话,可以在弹窗中勾选上不再弹窗,或者在应用设置中,关闭每日弹窗都是可以的。下面来写这个功能。
正文
说到弹窗我就想起来我之前的应用更新弹窗那一篇文章了,那么这个功能怎么来写呢?其实也不难,首先想清楚这个弹窗要什么东西。
① 是弹窗的背景,我希望每一天都不一样,那么就可以采用必应的每日一图。
② 弹窗可关闭不再弹出,可以通过缓存的方式判断处理,
③ 弹窗上面显示的值,可以通过主页面其它接口先获取到返回值,然后在弹窗中显示,
④ 每日只弹出一次,这个就是要在每日弹出弹窗时,存储一个时间戳缓存,后面再进入APP时判断时间大小就可以了。
这么一看,目标就明确了,下面进入实操环节。
一、弹窗背景
还记得之前我在写壁纸页面的时候,把必应的请求放到壁纸管理页面了,那么主页面就没有请求了,而我又需要这个请求获取每日的壁纸url。所以在欢迎页面新增了一个请求。
打开SplashContract,新增如下代码:
/** * 获取必应 每日一图 */ public void biying() { ApiService service = ServiceGenerator.createService(ApiService.class, 1); service.biying().enqueue(new NetCallBack<BiYingImgResponse>() { @Override public void onSuccess(Call<BiYingImgResponse> call, Response<BiYingImgResponse> response) { if (getView() != null) { getView().getBiYingResult(response); } } @Override public void onFailed() { if (getView() != null) { getView().getDataFailed(); } } }); }
/** * 获取必应每日一图返回 * @param response BiYingImgResponse */ void getBiYingResult(Response<BiYingImgResponse> response);
增加位置如下:
然后进入SplashActivity,重写getBiYingResult方法,代码如下:
/** * 必应壁纸数据返回 * * @param response BiYingImgResponse */ @Override public void getBiYingResult(Response<BiYingImgResponse> response) { if (response.body().getImages() != null) { //得到的图片地址是没有前缀的,所以加上前缀否则显示不出来 String biyingUrl = "http://cn.bing.com" + response.body().getImages().get(0).getUrl(); SPUtils.putString(Constant.EVERYDAY_TIP_IMG,biyingUrl,context); } else { ToastUtils.showShortToast(context, "未获取到必应的图片"); } }
这里你会发现Constant.EVERYDAY_TIP_IMG,没有这个属性值,那么就到Constant中去创建。
/** * 每日提示弹窗的背景图 */ public static final String EVERYDAY_TIP_IMG = "everydayTipImg"; /** * 每日提示弹窗是否弹出 */ public static final String EVERYDAY_POP_BOOLEAN = "everydayPopBoolean";
在Constant里面增加这两个系统变量,注释已经说明了这两个变量的用途了。
下面在layout中新建一个dialog_everyday_tip.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" android:layout_width="@dimen/dp_270" android:layout_height="wrap_content" android:orientation="vertical"> <!--弹窗背景图--> <com.google.android.material.imageview.ShapeableImageView android:id="@+id/iv_dialog_bg" android:layout_width="match_parent" android:layout_height="420dp" android:foreground="@drawable/shape_dialog_foreground_bg_12" android:scaleType="fitXY" android:src="@drawable/img_5" app:shapeAppearanceOverlay="@style/roundedImageStyle" /> <RelativeLayout android:layout_width="match_parent" android:layout_height="420dp" android:padding="@dimen/dp_12"> <TextView android:id="@+id/tv_week" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="星期四" android:textColor="@color/white" android:textSize="@dimen/sp_20" android:textStyle="bold" /> <!--温度--> <TextView android:id="@+id/tv_temperature" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/tv_week" android:text="温度" android:textColor="@color/white" android:textSize="@dimen/sp_48" /> <!--天气状态--> <TextView android:id="@+id/tv_weather_state" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/tv_temperature" android:text="天气" android:textColor="@color/white" android:textSize="@dimen/sp_20" android:typeface="monospace" /> <!--天气状态图标--> <ImageView android:id="@+id/iv_weather_state" android:layout_width="@dimen/dp_80" android:layout_height="@dimen/dp_80" android:layout_alignParentRight="true" android:src="@mipmap/icon_100" /> <!--降水预告--> <TextView android:id="@+id/tv_precipitation" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/iv_weather_state" android:layout_alignParentRight="true" android:layout_marginTop="@dimen/dp_10" android:drawableLeft="@mipmap/icon_weather_prec" android:drawablePadding="4dp" android:text="降水预告" android:textColor="@color/white" android:textSize="@dimen/sp_12" /> <!--温差提示--> <TextView android:id="@+id/tv_temp_difference" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/tv_weather_state" android:layout_marginTop="@dimen/dp_100" android:text="温差提示" android:textColor="@color/white" android:textSize="@dimen/sp_18" android:typeface="monospace" /> <!--不再弹出--> <com.google.android.material.checkbox.MaterialCheckBox android:id="@+id/cb_no_pop_up" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:text="不再弹出" android:textSize="@dimen/sp_16" app:useMaterialThemeColors="true" app:buttonTint="@color/gray" android:textColor="@color/gray" /> </RelativeLayout> <View android:layout_centerHorizontal="true" android:layout_below="@+id/iv_dialog_bg" android:background="@color/white" android:layout_width="@dimen/dp_1" android:layout_height="@dimen/dp_12"/> <ImageView android:id="@+id/iv_close" android:layout_width="@dimen/dp_24" android:layout_height="@dimen/dp_24" android:layout_below="@+id/iv_dialog_bg" android:layout_centerHorizontal="true" android:layout_marginTop="@dimen/dp_12" android:src="@mipmap/icon_close_dialog" /> </RelativeLayout>
预览图如下所示(里面的图标没有的话可以去我的源码里面下载,或者自行下载一个,因为是白色的所示我贴了也看不见,CSDN中,不开会员的人无法修改文章的主题颜色,免费的主题,改不了博文的颜色,这一点我觉得很坑,非要你开个会员,吃相太难看了)
这里面用到了一个style,在app的styles.xml中增加如下代码:
<!-- 圆角图片 --> <style name="roundedImageStyle"> <item name="cornerFamily">rounded</item> <item name="cornerSize">12dp</item> </style>
在drawable下新建一个shape_dialog_foreground_bg_12.xml文件,代码如下:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <corners android:radius="@dimen/dp_12" /> <solid android:color="@color/transparent_bg_1" /> </shape>
在app的colors.xml中新增一个颜色
<color name="transparent_bg_1">#20000000</color><!--黑色半透明 一-->
布局有了,那么就是先改变背景,再增加数据。
二、每天第一次弹窗
下面进入到MainActivity中,将检查版本更新的方法移动一个位置
因为自动更新的弹窗也是在每日第一次才弹出,所以公用,不过也要修改一下checkAppVersion里面的逻辑才行。修改后代码如下:
/** * 检查APP版本 */ private void checkAppVersion() { AppVersion appVersion = LitePal.find(AppVersion.class, 1);//读取第一条数据 Log.d("appVersion", new Gson().toJson(appVersion.getVersionShort())); if (AppStartUpUtils.isTodayFirstStartApp(context)) {//今天第一次打开APP if (!appVersion.getVersionShort().equals(APKVersionInfoUtils.getVerName(context))) {//提示更新 //更新提示弹窗 showUpdateAppDialog(appVersion.getInstall_url(), appVersion.getChangelog()); } //设置每日提示弹窗 setTipDialog(); } }
之前是判断可不可以更新,再判断是否为第一次,现在判断是否为第一次打开。
然后下面的重点就是这个setTipDialog方法了
/** * 设置每日弹窗 */ private void setTipDialog() { boolean isShow = SPUtils.getBoolean(Constant.EVERYDAY_POP_BOOLEAN, true, context); if (isShow) { new Handler().postDelayed(new Runnable() { @Override public void run() { //当所有数据加载完成之后显示弹窗 if (everyDayTipDialog != null) { return; } //弹出每日提醒弹窗 showEveryDayTipDialog(); } },1000); } }
这里用到那么那个系统变量,判断是否可以弹窗这个弹窗,然后延时弹出。
三、弹出每日提示弹窗
/** * 每日提示弹窗 */ private void showEveryDayTipDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(context) .addDefaultAnimation()//默认弹窗动画 .setCancelable(false) .setText(R.id.tv_week, DateUtils.getWeekOfDate(new Date()))//星期 .setText(R.id.tv_weather_state, dialogWeatherState)//天气状态 .setText(R.id.tv_precipitation, dialogPrecipitation)//降水预告 .setText(R.id.tv_temp_difference, WeatherUtil.differenceTempTip(dialogTempHeight, dialogTempLow))//温差提示信息 .setContentView(R.layout.dialog_everyday_tip)//载入布局文件 .setWidthAndHeight(SizeUtils.dp2px(context, 270), ViewGroup.LayoutParams.WRAP_CONTENT)//设置弹窗宽高 .setOnClickListener(R.id.iv_close, v -> {//关闭 everyDayTipDialog.dismiss(); }); everyDayTipDialog = builder.create(); String imgUrl = SPUtils.getString(Constant.EVERYDAY_TIP_IMG, "", context); ShapeableImageView bg = everyDayTipDialog.getView(R.id.iv_dialog_bg); Glide.with(context).load(imgUrl).into(bg); //温度 Typeface typeface = Typeface.createFromAsset(getAssets(), "fonts/Roboto-Light.ttf"); TextView temp = everyDayTipDialog.getView(R.id.tv_temperature); temp.setTypeface(typeface); temp.setText(dialogTemp + "℃"); //设置天气状态图标 ImageView weatherStateIcon = everyDayTipDialog.getView(R.id.iv_weather_state); WeatherUtil.changeIcon(weatherStateIcon, dialogWeatherStateCode); //不再弹出 MaterialCheckBox cb = everyDayTipDialog.getView(R.id.cb_no_pop_up); cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { SPUtils.putBoolean(Constant.EVERYDAY_POP_BOOLEAN, false, context); } else { SPUtils.putBoolean(Constant.EVERYDAY_POP_BOOLEAN, true, context); } } }); everyDayTipDialog.show(); }
这里面的代码就是先显示一些要的数据,通过缓存拿到必应的url设置背景,然后在弹窗的底部有一个选中框,选中后再关闭这个弹窗,那么这个弹窗以后都不会再弹出了,除非你再应用设置中进行打开。
到这一步,弹窗就出现了。
四、弹窗的开关
既然是增加用户的体验,那么自然要让用户可以自行控制,于是,我在新增了一个应用设置页面。在ui包下新建一个Empty Activity。命名为SettingActivity。它的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:orientation="vertical" android:fitsSystemWindows="true" android:background="@color/gray_white" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.SettingActivity"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="@color/white" app:contentInsetLeft="@dimen/dp_16" android:elevation="@dimen/dp_10" app:layout_constraintEnd_toEndOf="parent" app:layout_scrollFlags="scroll|enterAlways" app:navigationIcon="@mipmap/icon_return" app:popupTheme="@style/ThemeOverlay.AppCompat.Light"> <!--标题--> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="应用设置" android:textColor="@color/black" android:textSize="@dimen/sp_18" /> </androidx.appcompat.widget.Toolbar> <!--每日一图--> <LinearLayout android:layout_marginTop="@dimen/dp_12" android:background="@color/white" android:paddingLeft="@dimen/dp_16" android:paddingRight="@dimen/dp_16" android:paddingTop="@dimen/dp_8" android:paddingBottom="@dimen/dp_8" android:gravity="center_vertical" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:text="每日弹窗" android:textColor="@color/black" android:textSize="@dimen/sp_16" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content"/> <com.llw.mvplibrary.view.SwitchButton android:id="@+id/wb_everyday" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout> </LinearLayout>
整体来看这个页面目前只有一个管理每日弹窗的功能,有些孤独,不过后续我可能会加入其他的一些功能,敬请期待。
下面进入SettingActivity。里面的代码如下:
package com.llw.goodweather.ui; import android.os.Bundle; import androidx.appcompat.widget.Toolbar; import com.llw.goodweather.R; import com.llw.goodweather.utils.Constant; import com.llw.goodweather.utils.SPUtils; import com.llw.goodweather.utils.StatusBarUtil; import com.llw.mvplibrary.base.BaseActivity; import com.llw.mvplibrary.view.SwitchButton; import org.litepal.util.Const; import butterknife.BindView; import butterknife.ButterKnife; /** * 应用设置页面 * * @author llw */ public class SettingActivity extends BaseActivity { @BindView(R.id.toolbar) Toolbar toolbar; @BindView(R.id.wb_everyday) SwitchButton wbEveryday; @Override public void initData(Bundle savedInstanceState) { //白色状态栏 StatusBarUtil.setStatusBarColor(context, R.color.white); //黑色字体 StatusBarUtil.StatusBarLightMode(context); Back(toolbar); boolean isChecked = SPUtils.getBoolean(Constant.EVERYDAY_POP_BOOLEAN,true,context); wbEveryday.setChecked(isChecked); wbEveryday.setOnCheckedChangeListener(new SwitchButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(SwitchButton view, boolean isChecked) { if(isChecked){ SPUtils.putBoolean(Constant.EVERYDAY_POP_BOOLEAN,true,context); }else { SPUtils.putBoolean(Constant.EVERYDAY_POP_BOOLEAN,false,context); } } }); } @Override public int getLayoutId() { return R.layout.activity_setting; } }
就是通过控件改变缓存值,一语中的。
然后就是在window_add.xml中增加一个TextView
<TextView android:id="@+id/tv_setting" android:gravity="center" android:layout_width="@dimen/dp_140" android:layout_height="@dimen/dp_48" android:text="应用设置" android:foreground="@drawable/bg_white" android:textColor="@color/black" android:textSize="@dimen/sp_16"/>
如下图所示
然后进入到MainActivity,在showAddWindow方法中。
绑定视图id
增加点击跳转事件
然后代码就写完了,是不是一气呵成呢?运行效果图如下:
这个GIF,之前我是为了测试所以没有加上每日第一次打开的限制,你只要按照博客来写就可以了。
文末
来者可追,文过饰非。写博客和写代码的思路都要清晰才行,还是要加油,菜鸟多飞,山高水长,后会有期~