上一篇:Android 天气APP(十六)热门城市 - 海外城市
页面标题优化、添加加载弹窗
新版-------------------
在上一篇文章中完成了关于添加城市时通过搜索城市的方式,本篇文章就是对UI上进行一些优化,来看看有那些优化。
一、主页面标题栏优化
当前主页面布局activity_main.xml的标题代码如下:
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/materialToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:ellipsize="middle"
android:singleLine="true"
android:text="城市天气"
android:textColor="@color/white"
android:textSize="@dimen/sp_16" />
</com.google.android.material.appbar.MaterialToolbar>
可以看到这里是用的一个MaterialToolbar,里面放置了TextView,将TextView作为标题,现在我们去掉这个TextView,直接使用MaterialToolbar,修改后代码如下所示:
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/materialToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:title="城市天气"
app:titleTextColor="@color/white"
app:titleCentered="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
然后在代码中我们滑动的时候就要修改MaterialToolbar的title的内容,看一下MainActivity。
这里有报错,很正常,因为这个TextView已经被删掉了,那么我们替换一下,如下图所示:
还有一个地方需要修改
这里将setTitle的内容改成城市天气。
下面运行一下效果就和之前是差不多的了。
二、加载等待弹窗
现在进入主页面时会请求网络数据,然后会发现需要等一段时间才会显示所有数据,为了增加用户体验,现在增加一个加载弹窗,在请求网络数据的事情显示加载弹窗,然后在数据返回时隐藏弹窗,为了更好的时候,我将这个弹窗直接写到BaseActivity中,因为BaseActivity是在library模块中,所以下面的代码都是在library模块中增加。
首先在main下创建一个res文件夹,res下创建一个values文件夹,values文件夹下创建一个styles.xml文件,里面的代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--加载弹窗的样式-->
<style name="loading_dialog" parent="@android:style/Theme.Dialog">
<item name="android:windowFrame">@null</item>
<item name="android:windowIsFloating">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowNoTitle">true</item>
<item name="android:background">@null</item>
<item name="android:windowBackground">@null</item>
<item name="android:backgroundDimEnabled">false</item>
</style>
</resources>
这是弹窗的样式,下面在res下创建一个drawable文件夹,drawable文件夹下创建一个shape_loading_bg.xml文件,代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="12dp" />
<solid android:color="#90000000" />
</shape>
这是弹窗的背景样式,drawable下还有一个ic_loading.png的图标,你可以去我的源码中拿,贴出来尺寸格式就变了,下面就是写代码,在com.llw.library包下新建一个view包,里面创建一个LoadingView类,代码如下:
public class LoadingView extends androidx.appcompat.widget.AppCompatImageView {
private int mCenterRotateX;//图片旋转点x
private int mCenterRotateY;//图片旋转点y
private LoadingRunnable mRunnable;
public LoadingView(Context context) {
this(context, null);
}
public LoadingView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public LoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
setScaleType(ScaleType.MATRIX);
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_loading);
setImageBitmap(bitmap);
mCenterRotateX = bitmap.getWidth() / 2;
mCenterRotateY = bitmap.getHeight() / 2;
}
/**
* onDraw()之前调用
*/
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (mRunnable==null){
mRunnable=new LoadingRunnable(this);
}
if (!mRunnable.isLoading){
mRunnable.start();
}
}
/**
* view销毁时调用
*/
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (mRunnable!=null){
mRunnable.stop();
}
mRunnable=null;
}
class LoadingRunnable implements Runnable {
private boolean isLoading;
private final Matrix mMatrix;
private final SoftReference<LoadingView> mLoadingViewSoftReference;
private float mDegrees = 0f;
public LoadingRunnable(LoadingView loadingView) {
mLoadingViewSoftReference = new SoftReference<LoadingView>(loadingView);
mMatrix = new Matrix();
}
@Override
public void run() {
if (mLoadingViewSoftReference.get().mRunnable != null) {
mDegrees += 30f;
mMatrix.setRotate(mDegrees, mCenterRotateX, mCenterRotateY);
mLoadingViewSoftReference.get().setImageMatrix(mMatrix);
if (mDegrees==360){
mDegrees=0f;
}
if (isLoading) {
mLoadingViewSoftReference.get().postDelayed(mLoadingViewSoftReference.get().mRunnable, 100);
}
}
}
public void stop() {
isLoading = false;
}
public void start() {
isLoading = true;
if (mLoadingViewSoftReference.get().mRunnable != null) {
mLoadingViewSoftReference.get().postDelayed(mLoadingViewSoftReference.get().mRunnable, 100);
}
}
}
}
这是一个旋转图片的View, 下面在view包下新建一个LoadingTextView类,代码如下:
public class LoadingTextView extends AppCompatTextView {
private LinearGradient mLinearGradient;
private Matrix mGradientMatrix;
private int mViewWidth = 0;
private int mTranslate = 0;
public LoadingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if (mViewWidth == 0) {
mViewWidth = getMeasuredWidth();
if (mViewWidth > 0) {
Paint mPaint = getPaint();
mLinearGradient = new LinearGradient(-mViewWidth, 0, 0, 0,
new int[]{0x33ffffff, 0xff3286ED, 0x33ffffff},
new float[]{0, 0.6f, 1}, Shader.TileMode.CLAMP);
mPaint.setShader(mLinearGradient);
mGradientMatrix = new Matrix();
}
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mGradientMatrix != null) {
mTranslate += mViewWidth / 10;
if (mTranslate > 2 * mViewWidth) {
mTranslate = -mViewWidth;
}
mGradientMatrix.setTranslate(mTranslate, 0);
mLinearGradient.setLocalMatrix(mGradientMatrix);
postInvalidateDelayed(20);
}
}
}
这里是加载弹窗的提示文字,里面的文字颜色是跑马灯效果,下面在res下新建一个layout文件夹,layout下新建一个dialog_loading.xml,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_loading"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_gravity="center"
android:background="@drawable/shape_loading_bg"
android:gravity="center"
android:orientation="vertical">
<!--旋转的图-->
<com.llw.library.view.LoadingView
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!--变色的字-->
<com.llw.library.view.LoadingTextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="12dp"
android:text="请稍后..."
android:textColor="#fff"
android:textSize="14sp" />
</LinearLayout>
现在关于加载弹窗的资源都准备好了,下面就是在BaseActivity中添加显示和隐藏加载弹窗的方法代码,如下所示:
private Dialog mDialog;
protected void showLoadingDialog() {
if (mDialog == null) {
mDialog = new Dialog(mContext, R.style.loading_dialog);
}
mDialog.setContentView(R.layout.dialog_loading);
mDialog.setCancelable(true);
mDialog.getWindow().setBackgroundDrawableResource(android.R.color.transparent);
mDialog.show();
}
protected void dismissLoadingDialog() {
if (mDialog != null) {
mDialog.dismiss();
}
mDialog = null;
}
下面我们就可以在MainActivity中使用了,在获取定位结果的时候显示这个弹窗,如图所示:
在空气质量数据返回的时候隐藏这个弹窗
这个弹窗你手动点击页面其他区域也会隐藏,问题不大,下面运行一下看看效果。
三、文章源码
欢迎 Star 和 Fork
第十七篇文章源码地址: GoodWeather-New-17
旧版-------------------
前言
在上一篇做了国外的热门城市数据的展示,这一篇就简单一些,增加国内的热门城市。
效果图
正文
① 修改API
在ApiService中修改hotCity这个接口,将固定地址里面的group分离出来,作为请求参数。
/**
* 热门城市(包含海外和国内)
*/
@GET("/top?key=3086e91d66c04ce588a7f538f917c7f4&number=50&lang=zh")
Call<HotCityResponse> hotCity(@Query("group") String group);
② 修改订阅器
在HotCityContract中增加一个参数
③ 创建选择弹窗
之前是在HotActivity中默认查询海外热门城市的,现在增加了一个参数,就需要用户去手动选择了,我们可以通过一个弹窗来进行选择。
在layout下创建
布局代码如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/dp_280"
android:layout_height="@dimen/dp_120"
android:background="@drawable/shape_white_5"
android:gravity="center"
android:orientation="vertical">
<!--国内-->
<TextView
android:id="@+id/tv_inland"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:foreground="@drawable/bg_white"
android:gravity="center"
android:text="国内热门城市"
android:textColor="@color/black"
android:textSize="@dimen/sp_18" />
<!--分割线-->
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:background="@color/gray" />
<!--海外-->
<TextView
android:id="@+id/tv_foreign"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:foreground="@drawable/bg_white"
android:gravity="center"
android:text="海外热门城市"
android:textColor="@color/black"
android:textSize="@dimen/sp_18" />
</LinearLayout>
然后就是使用了,我修改了一下LiWindow中的showCenterPopupWindow方法中的入参
将这个值放到外面就可以在调用的时候设置是否可以点击空白处关闭弹窗,为true是可以,false是不可以。
修改activity_hot_city.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:fitsSystemWindows="true"
android:id="@+id/lay_bg"
android:orientation="vertical"
android:layout_height="match_parent"
tools:context=".ui.HotCityActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/white"
app:layout_constraintEnd_toEndOf="parent"
app:navigationIcon="@mipmap/icon_return"
app:contentInsetLeft="@dimen/dp_16"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:popupTheme="@style/AppTheme.PopupOverlay">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textSize="@dimen/sp_16"
android:textColor="@color/black"
android:text="热门城市" />
</androidx.appcompat.widget.Toolbar>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
修改的布局有什么变化呢?就是里面的布局增加了id,还有就是改了颜色,
之后在HotCityActivity中初始化
上图中标出来的就是新增的,然后创建一个显示弹窗的方法
/**
* 显示选择类型弹窗
*/
private void showTypeWindow() {
liWindow = new LiWindow(context);
final View view = LayoutInflater.from(context).inflate(R.layout.window_hot_type, null);
TextView tvInland = view.findViewById(R.id.tv_inland);//国内
TextView tvForeign = view.findViewById(R.id.tv_foreign);//海外
tvInland.setOnClickListener(v -> {
type = 0;
initList(type);
showLoadingDialog();
mPresent.hotCity(context, "cn");
liWindow.closePopupWindow();
});
tvForeign.setOnClickListener(v -> {
type = 1;
initList(type);
showLoadingDialog();
mPresent.hotCity(context, "overseas");
liWindow.closePopupWindow();
});
liWindow.showCenterPopupWindow(view, SizeUtils.dp2px(context, 280), SizeUtils.dp2px(context, 120), false);
}
因为是要在页面启动的时候就出现这个弹窗,而popupWindow显示依赖activity,并且要等activity所有的生命周期方法全部执行完成才能显示,所以这里新开一个线程用于显示
④ 修改列表item布局
弹窗搞定之后就可以改动热门城市的列表item布局了,首先增加一个颜色
item_hot_city_list.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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<androidx.cardview.widget.CardView
android:id="@+id/item_hot_city"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dp_6"
android:foreground="@drawable/bg_white"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="@dimen/dp_8"
app:cardElevation="@dimen/dp_4">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<!--增加id-->
<ImageView
android:id="@+id/iv_mark"
android:layout_width="@dimen/dp_80"
android:layout_height="@dimen/dp_80"
android:background="@drawable/shape_orange_8"
android:gravity="center"
android:padding="@dimen/dp_20"
android:src="@mipmap/icon_hot_city" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingLeft="@dimen/dp_16">
<TextView
android:id="@+id/tv_hot_city_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="巴黎"
android:textColor="@color/black_3"
android:textSize="@dimen/sp_16"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_cnty_and_area"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_8"
android:text="巴黎"
android:textColor="@color/gray"
android:textSize="@dimen/sp_14"
android:textStyle="bold" />
</LinearLayout>
<!--增加id-->
<ImageView
android:id="@+id/iv_open"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/dp_12"
android:src="@mipmap/icon_open_orange" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
图标也是要修改的
因为是白色的你看不见很正常,你把页面的主题改成黑色就可以看到了。
icon_hot_city_china.ping
icon_open_blue.png
⑤ 修改列表适配器
修改HotCityAdapter
package com.llw.goodweather.adapter;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.llw.goodweather.R;
import com.llw.goodweather.bean.HotCityResponse;
import java.util.ArrayList;
import java.util.List;
/**
* 热门城市列表适配器
*/
public class HotCityAdapter extends BaseQuickAdapter<HotCityResponse.HeWeather6Bean.BasicBean, BaseViewHolder> {
private int mType;
// 增加一个item样式类型,在Activity中传入
public HotCityAdapter(int layoutResId, @Nullable List<HotCityResponse.HeWeather6Bean.BasicBean> data,int type) {
super(layoutResId, data);
this.mType = type;
}
@Override
protected void convert(BaseViewHolder helper, HotCityResponse.HeWeather6Bean.BasicBean item) {
ImageView ivMark = helper.getView(R.id.iv_mark);
ImageView ivOpen = helper.getView(R.id.iv_open);
if (mType == 0) {//国内
ivMark.setBackground(mContext.getResources().getDrawable(R.drawable.shape_blue_8));//背景
ivMark.setImageDrawable(mContext.getDrawable(R.mipmap.icon_hot_city_china));//图标
ivOpen.setImageDrawable(mContext.getDrawable(R.mipmap.icon_open_blue));//图标
} else {//国外
ivMark.setBackground(mContext.getResources().getDrawable(R.drawable.shape_orange_8));//背景
ivMark.setImageDrawable(mContext.getDrawable(R.mipmap.icon_hot_city));//图标
ivOpen.setImageDrawable(mContext.getDrawable(R.mipmap.icon_open_orange));//图标
}
helper.setText(R.id.tv_hot_city_name,item.getLocation())
.setText(R.id.tv_cnty_and_area,item.getCnty()+" —— "+item.getAdmin_area());
helper.addOnClickListener(R.id.item_hot_city);
}
}
⑥ 样式调整
然后回到HotCityActivity中首先是在initList方法中增加一个入参
这样传入的类型就会影响到适配器中的样式了,最后一步就是在getHotCityResult方法中对返回值中做数据的处理了。
新增部分的代码如下:
toolbar.setNavigationIcon(getResources().getDrawable(R.mipmap.icon_return_white));//返回箭头颜色
tvTitle.setTextColor(getResources().getColor(R.color.white));//标题颜色
if (type == 0) {//标题判断
tvTitle.setText("国内热门城市");
StatusBarUtil.setStatusBarColor(context, R.color.blue_one);//蓝色底状态栏
toolbar.setBackgroundColor(getResources().getColor(R.color.blue_one));//标题 蓝色
layBg.setBackgroundColor(getResources().getColor(R.color.shallow_blue));//背景 蓝色
} else {
tvTitle.setText("海外热门城市");
StatusBarUtil.setStatusBarColor(context, R.color.orange);//橙色底 状态栏
toolbar.setBackgroundColor(getResources().getColor(R.color.orange));//标题 橙色
layBg.setBackgroundColor(getResources().getColor(R.color.shallow_orange));//背景 橙色
}
运行一下,看看效果
源码地址:GoodWeather
欢迎 Star 和 Fork