效果图
开发流程
一、前情提要
最近收到一些用户的反馈,内容是背景更换这个功能用的不是很舒服,至于为什么不舒服,说不上来。之前我是奔着功能实现去做的,所以很多的细节并没有想的太多,思虑再三之后打算重新做一个更换背景的功能。
二、正式开发
从上面的效果图来看,一步一步分析,第一个就是点击主页面右上角的加号会出出现一个弹窗,这里我把原来的背景管理给隐藏掉了,新增了一个壁纸管理的TextView,所以先打开window_add.xml,在里面新增如下代码:
<TextView android:id="@+id/tv_wallpaper" 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方法中,初始化刚才的TextView
然后给一个点击事件
wallpaper.setOnClickListener(view -> {//壁纸管理 startActivity(new Intent(context, WallPaperActivity.class)); mPopupWindow.dismiss(); });
你会发现没有这个WallPaperActivity,所以在app下的ui包中新增WallPaperActivity,它的布局文件为activity_wall_paper.xml,现在我们可以通过主页面进入到这个壁纸管理页面了,首先看看这个页面的效果是怎么样的。
整理效果就是页面上滑动时顶部标题、和底部的浮动按钮上滑隐藏,下滑则显示。这里其实我分了两部分做,第一步是上面的标题,这个可以通过布局来解决,那就是协调布局。
现在该activity_wall_paper.xml
<?xml version="1.0" encoding="utf-8"?> <androidx.coordinatorlayout.widget.CoordinatorLayout 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="vertical" android:fitsSystemWindows="true" tools:context=".ui.WallPaperActivity"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appbar" android:layout_width="match_parent" android:layout_height="wrap_content" app:elevation="0dp" android:theme="@style/ThemeOverlay.AppCompat.Light"> <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:contentInsetLeft="@dimen/dp_16" 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> </com.google.android.material.appbar.AppBarLayout> <!--壁纸列表--> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv" android:layout_width="match_parent" android:layout_height="match_parent" android:overScrollMode="never" android:padding="@dimen/dp_5" app:layout_behavior="@string/appbar_scrolling_view_behavior" /> </androidx.coordinatorlayout.widget.CoordinatorLayout>
现在呢?我们上滑这个RecyclerView的时候,顶部的Toolbar就会向上隐藏,下滑就会马上显示出来,不过因为我们的RecyclerView里面并没有数据,如果要填充数据进去。
1. 列表数据填充
这里我是从网络上找到的一个免费的壁纸接口,这里也分享给大家,地址如下:
http://service.picasso.adesk.com/v1/vertical/vertical?limit=30&skip=180&adult=false&first=0&order=hot
可以直接在浏览器请求这个地址,然后拿到返回的Json数据生成一个实体bean。下面在app的bean包下新建一个WallPaperResponse.java
里面的代码如下:
package com.llw.goodweather.bean; import java.util.List; /** * 壁纸列表返回实体 * * @author llw */ public class WallPaperResponse { /** * msg : success * res : {"vertical":[{"preview":"http://img5.adesk.com/5e26fed9e7bce75ebd2c449d","thumb":"http://img5.adesk.com/5e26fed9e7bce75ebd2c449d?imageMogr2/thumbnail/!350x540r/gravity/Center/crop/350x540","img":"http://img5.adesk.com/5e26fed9e7bce75ebd2c449d?imageMogr2/thumbnail/!720x1280r/gravity/Center/crop/720x1280","views":0,"cid":["4ef0a35c0569795756000000"],"rule":"?imageMogr2/thumbnail/!$<Width>x$<Height>r/gravity/Center/crop/$<Width>x$<Height>","ncos":6,"rank":51169,"source_type":"vertical","tag":[],"url":[],"wp":"http://img5.adesk.com/5e26fed9e7bce75ebd2c449d","xr":false,"cr":false,"favs":830,"atime":1.580187618E9,"id":"5e26fed9e7bce75ebd2c449d","store":"qiniu","desc":""},{"preview":"http://img5.adesk.com/5ed61fd2e7bce75e7ef4c0ed","thumb":"http://img5.adesk.com/5ed61fd2e7bce75e7ef4c0ed?imageMogr2/thumbnail/!350x540r/gravity/Center/crop/350x540","img":"http://img5.adesk.com/5ed61fd2e7bce75e7ef4c0ed?imageMogr2/thumbnail/!720x1280r/gravity/Center/crop/720x1280","views":0,"cid":["4fb479f75ba1c65561000027"],"rule":"?imageMogr2/thumbnail/!$<Width>x$<Height>r/gravity/Center/crop/$<Width>x$<Height>","ncos":0,"rank":7860,"source_type":"vertical","tag":[],"url":[],"wp":"http://img5.adesk.com/5ed61fd2e7bce75e7ef4c0ed","xr":false,"cr":false,"favs":71,"atime":1.591337416E9,"id":"5ed61fd2e7bce75e7ef4c0ed","store":"qiniu","desc":""}]} * code : 0 */ private String msg; private ResBean res; private int code; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public ResBean getRes() { return res; } public void setRes(ResBean res) { this.res = res; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public static class ResBean { private List<VerticalBean> vertical; public List<VerticalBean> getVertical() { return vertical; } public void setVertical(List<VerticalBean> vertical) { this.vertical = vertical; } public static class VerticalBean { /** * preview : http://img5.adesk.com/5e26fed9e7bce75ebd2c449d * thumb : http://img5.adesk.com/5e26fed9e7bce75ebd2c449d?imageMogr2/thumbnail/!350x540r/gravity/Center/crop/350x540 * img : http://img5.adesk.com/5e26fed9e7bce75ebd2c449d?imageMogr2/thumbnail/!720x1280r/gravity/Center/crop/720x1280 * views : 0 * cid : ["4ef0a35c0569795756000000"] * rule : ?imageMogr2/thumbnail/!$<Width>x$<Height>r/gravity/Center/crop/$<Width>x$<Height> * ncos : 6 * rank : 51169 * source_type : vertical * tag : [] * url : [] * wp : http://img5.adesk.com/5e26fed9e7bce75ebd2c449d * xr : false * cr : false * favs : 830 * atime : 1.580187618E9 * id : 5e26fed9e7bce75ebd2c449d * store : qiniu * desc : */ private String preview; private String thumb; private String img; private int views; private String rule; private int ncos; private int rank; private String source_type; private String wp; private boolean xr; private boolean cr; private int favs; private double atime; private String id; private String store; private String desc; private List<String> cid; private List<?> tag; private List<?> url; public String getPreview() { return preview; } public void setPreview(String preview) { this.preview = preview; } public String getThumb() { return thumb; } public void setThumb(String thumb) { this.thumb = thumb; } public String getImg() { return img; } public void setImg(String img) { this.img = img; } public int getViews() { return views; } public void setViews(int views) { this.views = views; } public String getRule() { return rule; } public void setRule(String rule) { this.rule = rule; } public int getNcos() { return ncos; } public void setNcos(int ncos) { this.ncos = ncos; } public int getRank() { return rank; } public void setRank(int rank) { this.rank = rank; } public String getSource_type() { return source_type; } public void setSource_type(String source_type) { this.source_type = source_type; } public String getWp() { return wp; } public void setWp(String wp) { this.wp = wp; } public boolean isXr() { return xr; } public void setXr(boolean xr) { this.xr = xr; } public boolean isCr() { return cr; } public void setCr(boolean cr) { this.cr = cr; } public int getFavs() { return favs; } public void setFavs(int favs) { this.favs = favs; } public double getAtime() { return atime; } public void setAtime(double atime) { this.atime = atime; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getStore() { return store; } public void setStore(String store) { this.store = store; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } public List<String> getCid() { return cid; } public void setCid(List<String> cid) { this.cid = cid; } public List<?> getTag() { return tag; } public void setTag(List<?> tag) { this.tag = tag; } public List<?> getUrl() { return url; } public void setUrl(List<?> url) { this.url = url; } } } }
实体bean有了,下面就是拆分这个请求地址了。找到在mvplibrary下找到ServiceGenerator.java。
新增如下
然后再打开ApiService.java,里面新增一个接口
/** * 手机壁纸API * * @return WallPaperResponse 网络壁纸数据返回 */ @GET("/v1/vertical/vertical?limit=30&skip=180&adult=false&first=0&order=hot") Call<WallPaperResponse> getWallPaper();
下面就是订阅器了,在app中的contract包下新建一个WallPaperContract.java,里面的代码如下:
package com.llw.goodweather.contract; import com.llw.goodweather.api.ApiService; import com.llw.goodweather.bean.BiYingImgResponse; import com.llw.goodweather.bean.WallPaperResponse; import com.llw.mvplibrary.base.BasePresenter; import com.llw.mvplibrary.base.BaseView; import com.llw.mvplibrary.bean.AppVersion; import com.llw.mvplibrary.bean.WallPaper; import com.llw.mvplibrary.net.NetCallBack; import com.llw.mvplibrary.net.ServiceGenerator; import retrofit2.Call; import retrofit2.Response; /** * 壁纸订阅器 * * @author llw */ public class WallPaperContract { public static class WallPaperPresenter extends BasePresenter<IWallPaperView> { /** * 获取必应 每日一图 */ 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(); } } }); } /** * 获取壁纸数据 */ public void getWallPaper() { // 6 表示访问网络壁纸接口 ApiService service = ServiceGenerator.createService(ApiService.class, 6); service.getWallPaper().enqueue(new NetCallBack<WallPaperResponse>() { @Override public void onSuccess(Call<WallPaperResponse> call, Response<WallPaperResponse> response) { if (getView() != null) { getView().getWallPaperResult(response); } } @Override public void onFailed() { if (getView() != null) { getView().getDataFailed(); } } }); } } public interface IWallPaperView extends BaseView { /** * 获取必应每日一图返回 * @param response BiYingImgResponse */ void getBiYingResult(Response<BiYingImgResponse> response); /** * 壁纸数据返回 * @param response WallPaperResponse */ void getWallPaperResult(Response<WallPaperResponse> response); /** * 错误返回 */ void getDataFailed(); } }
订阅器也有了,现在回到WallPaperActivity ,继承MvpActivity,传入订阅器,然后实现里面的接口,代码如下:
package com.llw.goodweather.ui; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; import androidx.recyclerview.widget.RecyclerView; import com.llw.goodweather.R; import com.llw.goodweather.contract.WallPaperContract; import com.llw.mvplibrary.bean.WallPaper; import com.llw.mvplibrary.mvp.MvpActivity; import butterknife.BindView; import butterknife.ButterKnife; import retrofit2.Response; /** * 壁纸管理 * * @author llw */ public class WallPaperActivity extends MvpActivity<WallPaperContract.WallPaperPresenter> implements WallPaperContract.IWallPaperView { @Override public void initData(Bundle savedInstanceState) { } @Override public int getLayoutId() { return R.layout.activity_wall_paper; } @Override protected WallPaperContract.WallPaperPresenter createPresent() { return new WallPaperContract.WallPaperPresenter(); } @Override public void getBiYingResult(Response<BiYingImgResponse> response) { } @Override public void getWallPaperResult(Response<WallPaper> response) { } @Override public void getDataFailed() { } }
现在可以先不管这个Activity了,列表既然是显示壁纸,那么就需要一个item的布局,下面在app下的layout下面创建一个item_wallpaper_list.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="match_parent" android:id="@+id/item_wallpaper" android:layout_height="wrap_content" android:layout_margin="@dimen/dp_5" android:orientation="vertical"> <com.google.android.material.imageview.ShapeableImageView android:id="@+id/iv_wallpaper" android:layout_width="match_parent" android:layout_height="wrap_content" android:scaleType="centerCrop" app:shapeAppearanceOverlay="@style/roundedCornerStyle" /> </RelativeLayout>
你可能没有用过ShapeableImageView,没关系,首先在mvplibrary中的build.gradle中的dependencies闭包下新增一个依赖库
api 'com.google.android.material:material:1.2.0'//更强
我之前的是1.1.0,那么你可以改成1.2.0。然后同步到你的项目中。你就可以是material专属的UI控件了,你可能会问为什么要用这个控件,普通的ImageView不行吗?因为普通的ImageView没有圆角啊,说道圆角图片我相信你不会陌生,你可能想到自定义ImageView来实现、或者使用第三方库来实现,但是ShapeableImageView里面就自带了圆角的样式给你,惊不惊喜意不意外?好了,废话不多少了,你的布局中应该还有报错的地方才对。因为你少了一个roundedCornerStyle的样式。在mvplibrary下的styles.xml中,新增一个样式就可以了。
<!-- 圆角图片 --> <style name="roundedCornerStyle"> <item name="cornerFamily">rounded</item> <item name="cornerSize">@dimen/dp_16</item> </style>
可能你还注意到我这个item的高度是wrap_content,所以你看不到高度,那么为什么这样做呢?因为我要使用瀑布流,哪种错落感,会给用户不一样的体验,因为不设置高度,是因为需要动态设置ImageView的高度,来实现这个错落感。OK,下面该写这个Adapter了。在app的adapter包下新建一个WallPaperAdapter.java,相信这个代码你能看得懂。
package com.llw.goodweather.adapter; import android.util.Log; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.RelativeLayout; import androidx.annotation.Nullable; import com.baidu.panosdk.plugin.indoor.util.ScreenUtils; import com.bumptech.glide.Glide; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.google.android.material.imageview.ShapeableImageView; import com.llw.goodweather.R; import com.llw.goodweather.bean.WallPaperResponse; import java.util.List; /** * 壁纸列表适配器 * * @author llw */ public class WallPaperAdapter extends BaseQuickAdapter<WallPaperResponse.ResBean.VerticalBean, BaseViewHolder> { //定义一个item的高度列表 List<Integer> mHeightList; /** * 头部广告 */ private String Top = "top"; /** * 底部广告 */ private String Bottom = "bottom"; public WallPaperAdapter(int layoutResId, @Nullable List<WallPaperResponse.ResBean.VerticalBean> data, List<Integer> heightList) { super(layoutResId, data); this.mHeightList = heightList; } @Override protected void convert(BaseViewHolder helper, WallPaperResponse.ResBean.VerticalBean item) { ShapeableImageView imageView = helper.getView(R.id.iv_wallpaper); //获取imageView的LayoutParams RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) imageView.getLayoutParams(); layoutParams.height = dip2px(mHeightList.get(helper.getAdapterPosition())); //重新设置imageView的高度 imageView.setLayoutParams(layoutParams); if (Top.equals(item.getDesc()) || Bottom.equals(item.getDesc())) { imageView.setImageResource(R.mipmap.icon_logo); } else { Glide.with(mContext).load(item.getImg()).into(imageView); } helper.addOnClickListener(R.id.item_wallpaper); } // dp 转成 px private int dip2px(float dpVale) { final float scale = mContext.getResources().getDisplayMetrics().density; return (int) (dpVale * scale + 0.5f); } }
适配器也写完了,下面改渲染页面了,对不对。回到WallPaperActivity
/** * 标题 */ @BindView(R.id.toolbar) Toolbar toolbar; /** * 数据列表 */ @BindView(R.id.rv) RecyclerView rv; /** * AppBarLayout布局 */ @BindView(R.id.appbar) AppBarLayout appbar;
先绑定页面的控件,然后创建列表和适配器的对象
/** * 壁纸数据列表 */ private List<WallPaperResponse.ResBean.VerticalBean> mList = new ArrayList<>(); /** * 壁纸数据适配器 */ private WallPaperAdapter mAdapter; /** * item高度列表 */ private List<Integer> heightList = new ArrayList<>(); /** * 壁纸数量 */ private static final int WALLPAPER_NUM = 30; /** * 头部和底部的item数据 */ private WallPaperResponse.ResBean.VerticalBean topBean, bottomBean; /** * 必应的每日壁纸 */ private String biyingUrl = null;
新增一个初始化列表数据的方法。
/** * 初始化列表数据 */ private void initWallPaperList() { heightList.add(100); for (int i = 0; i < WALLPAPER_NUM; i++) { heightList.add(300); } heightList.add(100); mAdapter = new WallPaperAdapter(R.layout.item_wallpaper_list, mList, heightList); //瀑布流 StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); //设置布局管理 rv.setLayoutManager(manager); //设置数据适配器 rv.setAdapter(mAdapter); //请求数据 mPresent.getWallPaper(); //获取必应壁纸 mPresent.biying(); mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() { @Override public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) { } }); }
下面就该来处理返回的数据了。
/** * 必应壁纸数据返回 * * @param response BiYingImgResponse */ @Override public void getBiYingResult(Response<BiYingImgResponse> response) { if (response.body().getImages() != null) { //得到的图片地址是没有前缀的,所以加上前缀否则显示不出来 biyingUrl = "http://cn.bing.com" + response.body().getImages().get(0).getUrl(); Log.d("type-->", biyingUrl); } else { ToastUtils.showShortToast(context, "未获取到必应的图片"); } } /** * 网络壁纸数据返回 * * @param response WallPaperResponse */ @Override public void getWallPaperResult(Response<WallPaperResponse> response) { if (response.body().getMsg().equals(Constant.SUCCESS)) { List<WallPaperResponse.ResBean.VerticalBean> data = response.body().getRes().getVertical(); //创建头部和底部的两个广告item的假数据 topBean = new WallPaperResponse.ResBean.VerticalBean(); topBean.setDesc("top"); topBean.setImg(""); bottomBean = new WallPaperResponse.ResBean.VerticalBean(); bottomBean.setDesc("bottom"); bottomBean.setImg(""); //数据填充 if (data != null && data.size() > 0) { mList.clear(); //添加头部 mList.add(topBean); //添加主要数据 for (int i = 0; i < data.size(); i++) { mList.add(data.get(i)); } //添加尾部 mList.add(bottomBean); Log.d("list-->", new Gson().toJson(mList)); //根据数据数量来刷新列表 mAdapter.notifyItemInserted(mList.size()); //删除数据库中的数据 LitePal.deleteAll(WallPaper.class); for (int i = 0; i < mList.size(); i++) { WallPaper wallPaper = new WallPaper(); wallPaper.setImgUrl(mList.get(i).getImg()); wallPaper.save(); } dismissLoadingDialog(); } else { ToastUtils.showShortToast(context, "壁纸数据为空"); dismissLoadingDialog(); } } else { dismissLoadingDialog(); ToastUtils.showShortToast(context, "未获取到壁纸数据"); } } @Override public void getDataFailed() { dismissLoadingDialog(); ToastUtils.showShortToast(context, "请求超时"); }
Constant中增加一个
/** * 成功 */ public static final String SUCCESS = "success";
你会发现网络壁纸的返回处理有些麻烦。不过注释都有了,应该看得懂。下面在mvplibrary中创建WallPaper.java
里面的代码很简单:
package com.llw.mvplibrary.bean; import org.litepal.crud.LitePalSupport; import java.util.List; /** * 壁纸表 * * @author llw */ public class WallPaper extends LitePalSupport { private String ImgUrl; public String getImgUrl() { return ImgUrl; } public void setImgUrl(String imgUrl) { ImgUrl = imgUrl; } }
然后改动assets下面的litepal.xml文件。
然后在WallPaperActivity的initData调用相关的方法。
@Override public void initData(Bundle savedInstanceState) { //加载弹窗 showLoadingDialog(); //高亮状态栏 StatusBarUtil.StatusBarLightMode(this); //左上角的返回 Back(toolbar); initWallPaperList(); }
下面请求就会有这个数据了,而且你上滑动就会有顶部的标题上隐藏的效果。
2. 浮动按钮的交互
下面加一个浮动按钮。在activity_wall_paper.xml中新增加一个
<!--浮动按钮--> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab_setting" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="end|bottom" android:layout_margin="@dimen/dp_20" android:clickable="true" android:src="@mipmap/icon_setting" app:backgroundTint="@color/white" app:backgroundTintMode="screen" app:borderWidth="@dimen/dp_0" app:hoveredFocusedTranslationZ="@dimen/dp_18" app:pressedTranslationZ="@dimen/dp_18" app:rippleColor="@color/blue_one" />
icon_setting的图标
然后在WallPaperActivity中新增
/** * 底部浮动按钮 */ @BindView(R.id.fab_setting) FloatingActionButton fabSetting;
然后在initWallPaperList方法中新增如下代码:
//滑动监听 rv.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); if (dy <= 0) { fabSetting.show(); } else {//上滑 fabSetting.hide(); } } });
通过滑动RecyclerView对浮动按钮进行控制。当然浮动按钮要是光是显示和隐藏自然远远不行,浮动按钮点击之后要怎么样呢?
要出现一个底部弹窗,供你选择哪种方式的壁纸。
下面在app的layout下新建一个dialog_bottom_wallpaper_setting.xml,布局代码如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/white" android:orientation="vertical"> <!--壁纸列表--> <LinearLayout android:id="@+id/lay_wallpaper_list" android:layout_width="match_parent" android:layout_height="wrap_content" android:foreground="?android:attr/selectableItemBackground" android:gravity="center" android:orientation="horizontal" android:padding="@dimen/dp_16"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="壁纸列表" android:textColor="@color/black_4" android:textSize="@dimen/sp_14" /> <ImageView android:id="@+id/iv_wallpaper_list" android:layout_width="@dimen/dp_24" android:layout_height="@dimen/dp_24" android:src="@mipmap/icon_selected" android:visibility="invisible" /> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:layout_marginLeft="@dimen/dp_16" android:layout_marginRight="@dimen/dp_16" android:background="@color/gray_white_2" /> <!--每日一图--> <LinearLayout android:id="@+id/lay_everyday_wallpaper" android:layout_width="match_parent" android:layout_height="wrap_content" android:foreground="?android:attr/selectableItemBackground" android:gravity="center" android:orientation="horizontal" android:padding="@dimen/dp_16"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="每日一图" android:textColor="@color/black_4" android:textSize="@dimen/sp_14" /> <ImageView android:id="@+id/iv_everyday_wallpaper" android:layout_width="@dimen/dp_24" android:layout_height="@dimen/dp_24" android:src="@mipmap/icon_selected" android:visibility="invisible" /> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:layout_marginLeft="@dimen/dp_16" android:layout_marginRight="@dimen/dp_16" android:background="@color/gray_white_2" /> <!--手动上传--> <LinearLayout android:id="@+id/lay_upload_wallpaper" android:layout_width="match_parent" android:layout_height="wrap_content" android:foreground="?android:attr/selectableItemBackground" android:gravity="center" android:orientation="horizontal" android:padding="@dimen/dp_16"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="手动上传" android:textColor="@color/black_4" android:textSize="@dimen/sp_14" /> <ImageView android:id="@+id/iv_upload_wallpaper" android:layout_width="@dimen/dp_24" android:layout_height="@dimen/dp_24" android:src="@mipmap/icon_selected" android:visibility="invisible" /> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="1dp" android:layout_marginLeft="@dimen/dp_16" android:layout_marginRight="@dimen/dp_16" android:background="@color/gray_white_2" /> <!--默认壁纸--> <LinearLayout android:id="@+id/lay_default_wallpaper" android:layout_width="match_parent" android:layout_height="wrap_content" android:foreground="?android:attr/selectableItemBackground" android:gravity="center" android:orientation="horizontal" android:padding="@dimen/dp_16"> <TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:text="默认壁纸" android:textColor="@color/black_4" android:textSize="@dimen/sp_14" /> <ImageView android:id="@+id/iv_default_wallpaper" android:layout_width="@dimen/dp_24" android:layout_height="@dimen/dp_24" android:src="@mipmap/icon_selected" android:visibility="invisible" /> </LinearLayout> </LinearLayout>
预览如下:
icon_selected图标:
下面来写这个弹窗。
回到WallPaperActivity。
初始化这个弹窗,注意这个导包是我自定义的,不是系统自带的。
import com.llw.mvplibrary.view.dialog.AlertDialog;
/** * 底部弹窗 */ AlertDialog bottomSettingDialog = null;
然后写一个方法用来显示弹窗以及里面的一些业务逻辑的处理。
/** * 壁纸底部弹窗弹窗 */ private void showSettingDialog(int type) { AlertDialog.Builder builder = new AlertDialog.Builder(context) .addDefaultAnimation()//默认弹窗动画 .setCancelable(true) .fromBottom(true) //载入布局文件 .setContentView(R.layout.dialog_bottom_wallpaper_setting) //设置弹窗宽高 .setWidthAndHeight(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) //壁纸列表 .setOnClickListener(R.id.lay_wallpaper_list, v -> { Intent intent = new Intent(context, ImageActivity.class); intent.putExtra("position", 0); startActivity(intent); bottomSettingDialog.dismiss(); //每日一图 }).setOnClickListener(R.id.lay_everyday_wallpaper, v -> { ToastUtils.showShortToast(context, "使用每日一图"); SPUtils.putString(Constant.WALLPAPER_URL, biyingUrl, context); //壁纸列表 SPUtils.putInt(Constant.WALLPAPER_TYPE, 2, context); bottomSettingDialog.dismiss(); //手动上传 }).setOnClickListener(R.id.lay_upload_wallpaper, v -> { startActivityForResult(CameraUtils.getSelectPhotoIntent(), SELECT_PHOTO); ToastUtils.showShortToast(context, "请选择图片"); bottomSettingDialog.dismiss(); //默认壁纸 }).setOnClickListener(R.id.lay_default_wallpaper, v -> { ToastUtils.showShortToast(context, "使用默认壁纸"); SPUtils.putInt(Constant.WALLPAPER_TYPE, 4, context);//使用默认壁纸 SPUtils.putString(Constant.WALLPAPER_URL, null, context); bottomSettingDialog.dismiss(); }); bottomSettingDialog = builder.create(); ImageView iv_wallpaper_list = (ImageView) bottomSettingDialog.getView(R.id.iv_wallpaper_list); ImageView iv_everyday_wallpaper = (ImageView) bottomSettingDialog.getView(R.id.iv_everyday_wallpaper); ImageView iv_upload_wallpaper = (ImageView) bottomSettingDialog.getView(R.id.iv_upload_wallpaper); ImageView iv_default_wallpaper = (ImageView) bottomSettingDialog.getView(R.id.iv_default_wallpaper); switch (type) { //壁纸列表 case 1: iv_wallpaper_list.setVisibility(View.VISIBLE); break; //每日一图 case 2: iv_everyday_wallpaper.setVisibility(View.VISIBLE); break; //手动上传 case 3: iv_upload_wallpaper.setVisibility(View.VISIBLE); break; //默认壁纸 case 4: iv_default_wallpaper.setVisibility(View.VISIBLE); break; default: iv_default_wallpaper.setVisibility(View.GONE); break; } bottomSettingDialog.show(); //弹窗关闭监听 bottomSettingDialog.setOnDismissListener(new DialogInterface.OnDismissListener() { @Override public void onDismiss(DialogInterface dialog) { fabSetting.show(); } }); }
可以看到通过一个type来控制当前的壁纸属于那种模式,然后在弹窗关闭的时候显示浮动按钮,我在Constant中定义了两个变量,一个用于保存壁纸的状态,一个用于保存壁纸的地址值。
/** * 壁纸地址 */ public static final String WALLPAPER_URL = "wallpaperUrl"; /** * 壁纸类型 1 壁纸列表 2 每日一图 3 手动上传 4 默认壁纸 */ public static final String WALLPAPER_TYPE = "wallpaperType";
里面用到过一个工具类CameraUtils,代码如下:
package com.llw.goodweather.utils; import android.annotation.TargetApi; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import android.media.ExifInterface; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.util.Log; import android.widget.ImageView; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; /** * 相机、相册工具类 * * @author llw */ public class CameraUtils { public static Intent getTakePhotoIntent(Context context, File outputImagepath) { //获取系統版本 int currentapiVersion = Build.VERSION.SDK_INT; // 激活相机 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 判断存储卡是否可以用,可用进行存储 if (hasSdcard()) { if (currentapiVersion < 24) { // 从文件中创建uri Uri uri = Uri.fromFile(outputImagepath); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); } else { //兼容android7.0 使用共享文件的形式 ContentValues contentValues = new ContentValues(1); contentValues.put(MediaStore.Images.Media.DATA, outputImagepath.getAbsolutePath()); Uri uri = context.getApplicationContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); } } return intent; } public static Intent getSelectPhotoIntent() { Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); return intent; } /* * 判断sdcard是否被挂载 */ public static boolean hasSdcard() { return Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED); } /** * 4.4及以上系统处理图片的方法 */ @TargetApi(Build.VERSION_CODES.KITKAT) public static String getImgeOnKitKatPath(Intent data, Context context) { String imagePath = null; Uri uri = data.getData(); Log.d("uri=intent.getData :", "" + uri); if (DocumentsContract.isDocumentUri(context, uri)) { String docId = DocumentsContract.getDocumentId(uri); //数据表里指定的行 Log.d("getDocumentId(uri) :", "" + docId); Log.d("uri.getAuthority() :", "" + uri.getAuthority()); if ("com.android.providers.media.documents".equals(uri.getAuthority())) { String id = docId.split(":")[1]; String selection = MediaStore.Images.Media._ID + "=" + id; imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, context); } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId)); imagePath = getImagePath(contentUri, null, context); } } else if ("content".equalsIgnoreCase(uri.getScheme())) { imagePath = getImagePath(uri, null, context); } return imagePath; } /** * 通过uri和selection来获取真实的图片路径,从相册获取图片时要用 */ public static String getImagePath(Uri uri, String selection, Context context) { String path = null; Cursor cursor = context.getContentResolver().query(uri, null, selection, null, null); if (cursor != null) { if (cursor.moveToFirst()) { path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); } return path; } //改变拍完照后图片方向不正的问题 public static void ImgUpdateDirection(String filepath, Bitmap orc_bitmap, ImageView iv) { int digree = 0;//图片旋转的角度 //根据图片的URI获取图片的绝对路径 Log.i("tag", ">>>>>>>>>>>>>开始"); //String filepath = ImgUriDoString.getRealFilePath(getApplicationContext(), uri); Log.i("tag", "》》》》》》》》》》》》》》》" + filepath); //根据图片的filepath获取到一个ExifInterface的对象 ExifInterface exif = null; try { exif = new ExifInterface(filepath); Log.i("tag", "exif》》》》》》》》》》》》》》》" + exif); if (exif != null) { // 读取图片中相机方向信息 int ori = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED); // 计算旋转角度 switch (ori) { case ExifInterface.ORIENTATION_ROTATE_90: digree = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: digree = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: digree = 270; break; default: digree = 0; break; } } //如果图片不为0 if (digree != 0) { // 旋转图片 Matrix m = new Matrix(); m.postRotate(digree); orc_bitmap = Bitmap.createBitmap(orc_bitmap, 0, 0, orc_bitmap.getWidth(), orc_bitmap.getHeight(), m, true); } if (orc_bitmap != null) { iv.setImageBitmap(orc_bitmap); } } catch (IOException e) { e.printStackTrace(); exif = null; } } /** * 4.4以下系统处理图片的方法 */ public static String getImageBeforeKitKatPath(Intent data, Context context) { Uri uri = data.getData(); String imagePath = getImagePath(uri, null, context); return imagePath; } //比例压缩 public static Bitmap comp(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos); if (baos.toByteArray().length / 1024 > 5120) {//判断如果图片大于5M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出 baos.reset();//重置baos即清空baos image.compress(Bitmap.CompressFormat.JPEG, 50, baos);//这里压缩50%,把压缩后的数据存放到baos中 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray()); BitmapFactory.Options newOpts = new BitmapFactory.Options(); //开始读入图片,此时把options.inJustDecodeBounds 设回true了 newOpts.inJustDecodeBounds = true; Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts); newOpts.inJustDecodeBounds = false; int w = newOpts.outWidth; int h = newOpts.outHeight; //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为 float hh = 800f;//这里设置高度为800f float ww = 480f;//这里设置宽度为480f //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可 int be = 1;//be=1表示不缩放 if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放 be = (int) (newOpts.outWidth / ww); } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放 be = (int) (newOpts.outHeight / hh); } if (be <= 0) be = 1; newOpts.inSampleSize = be;//设置缩放比例 newOpts.inPreferredConfig = Bitmap.Config.RGB_565;//降低图片从ARGB888到RGB565 //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了 isBm = new ByteArrayInputStream(baos.toByteArray()); bitmap = BitmapFactory.decodeStream(isBm, null, newOpts); return bitmap;//压缩好比例大小后再进行质量压缩 } }
因为第三个是手动上传,所以会打开你的本地相册,当你选择一个图片之后,需要拿到返回的数据。所以要重写onActivityResult,在这个方法里面获取到图片的路径,然后放到缓存里,这时候你的壁纸类型就是手动上传的壁纸了。
/** * Activity返回结果 * * @param requestCode * @param resultCode * @param data */ @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode) { //打开相册后返回 case SELECT_PHOTO: if (resultCode == RESULT_OK) { String imagePath = null; //判断手机系统版本号 if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT) { //4.4及以上系统使用这个方法处理图片 imagePath = CameraUtils.getImgeOnKitKatPath(data, this); } else { imagePath = CameraUtils.getImageBeforeKitKatPath(data, this); } displayImage(imagePath); } Log.d("result-->", requestCode + " " + resultCode + " " + data.getData().toString()); break; default: Log.d("result-->", requestCode + " " + resultCode + " " + data.getData().toString()); break; } } /** * 从相册获取完图片(根据图片路径显示图片) */ private void displayImage(String imagePath) { if (!TextUtils.isEmpty(imagePath)) { //将本地上传选中的图片地址放入缓存,当手动定义开关打开时,取出缓存中的图片地址,显示为背景 SPUtils.putInt(Constant.WALLPAPER_TYPE, 3, context); SPUtils.putString(Constant.WALLPAPER_URL, imagePath, context); ToastUtils.showShortToast(context, "已更换为你选择的图片"); } else { SPUtils.putInt(Constant.WALLPAPER_TYPE, 0, context); ToastUtils.showShortToast(context, "图片获取失败"); } }
至于默认壁纸,只要壁纸类型改为4,然后清空缓存中壁纸地址就可以了。因为这个地址是MainActivity中用来显示背景的依据,没有了就会显示默认背景。
至于第二个每日一图,就是在点击的时候把通过结果返回的地址拼接之后,再放入缓存中。同样指定类型。当然最头痛的是这个壁纸列表,首先在当前页面我们已经可以看到这个壁纸列表数据了。那么我们可以通过点击item的时候跳转到查看该壁纸完整的页面。所以需要创建一个ImageActivity,在app的UI包下创建。
布局的代码如下:
<?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:background="@color/white" tools:context=".ui.ImageActivity"> <androidx.viewpager2.widget.ViewPager2 android:id="@+id/vp" android:layout_width="match_parent" android:layout_height="match_parent"/> <!--返回按钮--> <ImageView android:id="@+id/iv_back" android:layout_width="@dimen/dp_50" android:layout_height="@dimen/dp_50" android:layout_marginLeft="@dimen/dp_12" android:layout_marginTop="@dimen/dp_30" android:padding="@dimen/dp_8" android:background="@drawable/selector_bg_img" android:src="@mipmap/icon_image_return_white" /> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:layout_marginBottom="@dimen/dp_12" android:gravity="center" android:orientation="horizontal"> <!--设为壁纸--> <com.google.android.material.button.MaterialButton android:id="@+id/btn_setting_wallpaper" style="@style/Widget.MaterialComponents.Button.UnelevatedButton" android:layout_width="@dimen/dp_100" android:layout_height="@dimen/dp_32" android:layout_marginRight="@dimen/dp_6" android:insetTop="@dimen/dp_0" android:insetBottom="@dimen/dp_0" android:text="设为壁纸" android:theme="@style/Theme.MaterialComponents.Light.NoActionBar" app:backgroundTint="@color/white_2" app:cornerRadius="@dimen/dp_16" app:strokeColor="@color/white" app:strokeWidth="@dimen/dp_1" /> <!--下载壁纸--> <com.google.android.material.button.MaterialButton android:id="@+id/btn_download" style="@style/Widget.MaterialComponents.Button.UnelevatedButton" android:layout_width="@dimen/dp_100" android:layout_height="@dimen/dp_32" android:layout_marginLeft="@dimen/dp_6" android:insetTop="@dimen/dp_0" android:insetBottom="@dimen/dp_0" android:text="下载壁纸" android:theme="@style/Theme.MaterialComponents.Light.NoActionBar" app:backgroundTint="@color/about_bg_color" app:cornerRadius="@dimen/dp_16" /> </LinearLayout> </RelativeLayout>
因为布局用的是ViewPager2。它可以直接传RecyclerView.Adapter进去,让我们希望的是,进入之后可以左右滑动查看壁纸,所以用到这个,那么怎么实现呢?首先需要展示的item布局。在app下的layout中创建一个item_image_list.xml文件,里面的布局代码如下:
<?xml version="1.0" encoding="utf-8"?> <com.google.android.material.imageview.ShapeableImageView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/wallpaper" android:scaleType="centerCrop" android:layout_width="match_parent" android:layout_height="match_parent"/>
然后在ImageActivity中,需要继承BaseActivity。里面的完整代码如下:
package com.llw.goodweather.ui; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.ImageView; import androidx.annotation.Nullable; import androidx.viewpager2.widget.ViewPager2; import com.bumptech.glide.Glide; import com.chad.library.adapter.base.BaseQuickAdapter; import com.chad.library.adapter.base.BaseViewHolder; import com.google.android.material.button.MaterialButton; import com.google.gson.Gson; 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.goodweather.utils.ToastUtils; import com.llw.mvplibrary.base.BaseActivity; import com.llw.mvplibrary.bean.WallPaper; import org.litepal.LitePal; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import butterknife.BindView; import butterknife.OnClick; /** * 查看图片 * * @author llw */ public class ImageActivity extends BaseActivity { @BindView(R.id.iv_back) ImageView ivBack; @BindView(R.id.btn_setting_wallpaper) MaterialButton btnSettingWallpaper; @BindView(R.id.btn_download) MaterialButton btnDownload; @BindView(R.id.vp) ViewPager2 vp; List<WallPaper> mList = new ArrayList<>(); WallPaperAdapter mAdapter; String wallpaperUrl = null; private int position; private Bitmap bitmap; @Override public void initData(Bundle savedInstanceState) { showLoadingDialog(); //透明状态栏 StatusBarUtil.transparencyBar(context); //获取位置 position = getIntent().getIntExtra("position", 0); //获取数据 mList = LitePal.findAll(WallPaper.class); Log.d("list-->", "" + mList.size()); if (mList != null && mList.size() > 0) { for (int i = 0; i < mList.size(); i++) { if (mList.get(i).getImgUrl().equals("")) { mList.remove(i); } } } Log.d("list-->", "" + mList.size()); //RecyclerView实现方式 mAdapter = new WallPaperAdapter(R.layout.item_image_list, mList); Log.d("wallPaper", new Gson().toJson(mList)); //ViewPager2实现方式 vp.setAdapter(mAdapter); vp.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() { @Override public void onPageSelected(int position) { Log.d("position-->", "" + position); wallpaperUrl = mList.get(position).getImgUrl(); bitmap = getBitMap(wallpaperUrl); } }); mAdapter.notifyDataSetChanged(); vp.setCurrentItem(position, false); dismissLoadingDialog(); } @Override public int getLayoutId() { return R.layout.activity_image; } @OnClick({R.id.iv_back, R.id.btn_setting_wallpaper, R.id.btn_download}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.iv_back: finish(); break; //设置壁纸 case R.id.btn_setting_wallpaper: //放入缓存 SPUtils.putString(Constant.WALLPAPER_URL, wallpaperUrl, context); //壁纸列表 SPUtils.putInt(Constant.WALLPAPER_TYPE, 1, context); ToastUtils.showShortToast(context, "已设置"); break; //下载壁纸 case R.id.btn_download: saveImageToGallery(context, bitmap); break; default: break; } } /** * 壁纸适配器 */ public class WallPaperAdapter extends BaseQuickAdapter<WallPaper, BaseViewHolder> { public WallPaperAdapter(int layoutResId, @Nullable List<WallPaper> data) { super(layoutResId, data); } @Override protected void convert(BaseViewHolder helper, WallPaper item) { ImageView imageView = helper.getView(R.id.wallpaper); Glide.with(mContext).load(item.getImgUrl()).into(imageView); } } /** * 保存图片到本地相册 * * @param context 上下文 * @param bitmap bitmap * @return */ public boolean saveImageToGallery(Context context, Bitmap bitmap) { // 首先保存图片 String filePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "good_weather"; File appDir = new File(filePath); if (!appDir.exists()) { appDir.mkdir(); } String fileName = "wallpaper" + 1024 + ".jpg"; File file = new File(appDir, fileName); try { FileOutputStream fos = new FileOutputStream(file); //通过io流的方式来压缩保存图片 boolean isSuccess = bitmap.compress(Bitmap.CompressFormat.JPEG, 60, fos); fos.flush(); fos.close(); //把文件插入到系统图库 MediaStore.Images.Media.insertImage(context.getContentResolver(), file.getAbsolutePath(), fileName, null); //保存图片后发送广播通知更新数据库 Uri uri = Uri.fromFile(file); context.sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, uri)); if (isSuccess) { ToastUtils.showShortToast(context, "图片保存成功"); return true; } else { ToastUtils.showShortToast(context, "图片保存失败"); return false; } } catch (IOException e) { e.printStackTrace(); } ToastUtils.showShortToast(context, "图片保存失败"); return false; } /** * Url转Bitmap * * @param url * @return */ public Bitmap getBitMap(final String url) { //新启一个线程进行转换 new Thread(new Runnable() { @Override public void run() { URL imageurl = null; try { imageurl = new URL(url); } catch (MalformedURLException e) { e.printStackTrace(); } try { HttpURLConnection conn = (HttpURLConnection) imageurl.openConnection(); conn.setDoInput(true); conn.connect(); InputStream inputStream = conn.getInputStream(); bitmap = BitmapFactory.decodeStream(inputStream); inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } }).start(); return bitmap; } }
下面讲一下这个页面的业务逻辑。从WallPaperActivity中点击item活着是点击底部弹窗的时候传递点击的position过来。然后查询数据库中的壁纸表,移除掉ImgUrl为 “”的数据,因为这个数据是我手动加上去的,然后就是设置适配器给ViewPager2,滑动的时候获取选中页的图片地址,并将地址转成bitmap,然后当你点击底部的下载壁纸的时候,通过保存当前的bitmap到手机本地,页面的逻辑就讲完了。下面回到WallPaperActivity,在里面添加item的点击后的业务处理。
还差一步,那就是浮动按钮的点击事件没有写。
@OnClick(R.id.fab_setting) public void onViewClicked() { fabSetting.hide(); int type = SPUtils.getInt(Constant.WALLPAPER_TYPE, 4, context); showSettingDialog(type); }
那么到这里页面的代码就差不多写完了。累死我了,写完代码写博客,说真的写博客比写代码更累。下面就是回到MainActivity中去做壁纸的显示处理。在onResume方法中
方法代码如下:
/** * 更换壁纸 */ private void updateWallpaper() { String imgUrl = SPUtils.getString(Constant.WALLPAPER_URL, null, context); if (imgUrl != null) { Glide.with(context).load(imgUrl).into(bg); } else { Glide.with(context).load(R.drawable.img_5).into(bg); } }
OK,代码就写完了。
3. 其他优化
我记得我之前说过有空就要优化那个搜索城市页面的弹窗,之前用的原生的比较的丑。所以现在自己创建了一个布局,在app中的layout下创建一个dialog_tip.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:background="@drawable/shape_white_5" android:layout_width="@dimen/dp_270" android:layout_height="wrap_content"> <TextView android:id="@+id/tv_title" android:text="提示" android:padding="@dimen/dp_8" android:gravity="center" android:textSize="@dimen/sp_16" android:textColor="@color/black_4" android:layout_width="match_parent" android:layout_height="@dimen/dp_48"/> <TextView android:id="@+id/tv_content" android:text="内容" android:padding="@dimen/dp_20" android:gravity="center" android:textSize="@dimen/sp_14" android:textColor="@color/black_4" android:layout_width="match_parent" android:layout_height="wrap_content"/> <View android:background="@color/gray" android:layout_width="match_parent" android:layout_height="0.3dp"/> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/tv_cancel" android:text="取消" android:gravity="center" android:padding="@dimen/dp_12" android:textSize="@dimen/sp_14" android:foreground="@drawable/bg_white" android:textColor="@color/black_4" android:layout_weight="1" android:layout_width="@dimen/dp_0" android:layout_height="wrap_content"/> <View android:background="@color/gray" android:layout_width="0.3dp" android:layout_height="match_parent"/> <TextView android:id="@+id/tv_sure" android:text="确定" android:gravity="center" android:foreground="@drawable/bg_white" android:padding="@dimen/dp_12" android:textSize="@dimen/sp_14" android:textColor="@color/black_4" android:layout_weight="1" android:layout_width="@dimen/dp_0" android:layout_height="wrap_content"/> </LinearLayout> </LinearLayout>
进入SearchCityActivity
/** * 提示弹窗 */ private AlertDialog tipDialog = null; /** * 显示提示弹窗 * @param data 数据 * @param content 内容 */ private void showTipDialog(Object data, String content) { AlertDialog.Builder builder = new AlertDialog.Builder(context) .addDefaultAnimation() .setCancelable(true) .setContentView(R.layout.dialog_tip) .setWidthAndHeight(SizeUtils.dp2px(context, 270), LinearLayout.LayoutParams.WRAP_CONTENT) .setText(R.id.tv_content, content) .setOnClickListener(R.id.tv_cancel, v -> { tipDialog.dismiss(); }).setOnClickListener(R.id.tv_sure, v -> { //传入all则删除所有 if (ALL_RECORD.equals(data)) { flSearchRecords.setLimit(true); //清除所有数据 mRecordsDao.deleteUsernameAllRecords(); llHistoryContent.setVisibility(View.GONE); } else { //删除某一条记录 传入单个的position mRecordsDao.deleteRecord(recordList.get((Integer) data)); initTagFlowLayout(); } tipDialog.dismiss(); }); tipDialog = builder.create(); tipDialog.show(); }
调用的地方。长按删除单条数据
点击删除全部数据
4. 运行效果图
运行效果图如下所示:
三、文末
搞定了。后面还会更新的,未完待续。