简易的安卓天气app(一)——解析Json数据、数据类封装
简易的安卓天气app(二)——适配器、每小时数据展示
📌简易的安卓天气app(三)——城市管理、数据库操作
简易的安卓天气app(四)——搜索城市、完善页面

需求🏷️

当我们查看天气时,我们都拥有自己的手机,都知道现在的智能手机已经可以根据定位来自动查询所在位置的天气信息,这个依靠的就是手机的定位功能,此处的项目纯属练手,不会涉及定位操作,那一大堆步骤我直接放弃,所以这个功能我们可以偷鸡摸,,,不对,投机取巧从天气api接口想办法搞到。(源码见页尾✨)

本项目用到的天气api接口天气api
详细操作内容见第一篇-->🧿简易的安卓天气app(一)——解析Json数据<--

此接口当我们默认不拼接参数,直接访问会得到当前位置的温度Json数据,这不就弥补了我们在定位方面的缺陷了吗,那么很好,当我们加上参数之后(此api根据城市查询的参数需+&city=(城市名称))那么,当我们只加了参数却没有加参数值(&city=)他还是会根据当前位置进行定位,简直是太妙了。
所以接下来就是进行安卓SQLite数据库的一些操作,把当前位置信息保存到数据库中(城市名,城市温度,更新时间)
待实现效果:
gif

数据库成功插入当前位置数据信息(之前测试插入过几条的测试对照用数据):
位置商丘的信息成功插入,插入之后,后续要进行的操作就是实时更新数据库
mancity

涉及内容

  • SQLite数据库操作,创建数据库,新建表,增删查改操作
  • 适配器adapter添加点击事件
  • 页面传值跳转

    项目结构

    3
    其他内容暂不涉及,本文章只包含城市管理,添加城市到数据库等的一系列操作,前面基础内容移步到页首地址

    界面设计

    城市管理的界面设计city_manager.xml
    city_manager
    city_manager.xml界面布局源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
<?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="match_parent"
android:background="@mipmap/bg"
android:orientation="vertical">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:orientation="horizontal">

<ImageView
android:id="@+id/iv_add"
android:layout_width="35dp"
android:layout_height="30dp"
android:layout_marginLeft="20dp"
android:src="@drawable/add" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="城市管理"
android:layout_gravity="center"
android:layout_weight="1"
android:textColor="@color/purple_200"
android:textSize="20dp" />

<ImageView
android:id="@+id/iv_more"
android:layout_width="35dp"
android:layout_height="30dp"
android:layout_marginRight="20dp"
android:src="@drawable/more" />
</LinearLayout>

<View
android:layout_width="390dp"
android:layout_height="8px"
android:layout_gravity="center"
android:layout_marginBottom="5dp"
android:alpha="0.6"
android:background="@color/white" />

<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/city_manager"/>

</LinearLayout>

added_city.xml页面表示要展示的一个个城市数据:
city

源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?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="55dp"
android:textSize="20sp"
android:layout_marginLeft="40dp"
android:layout_marginRight="40dp"
android:layout_marginTop="5dp"
android:background="@drawable/blackground"
android:gravity="center_vertical"
android:paddingLeft="10dp"
>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="220dp"
android:layout_height="wrap_content"
android:id="@+id/city_city"
android:layout_marginLeft="10dp"
android:textSize="22dp"
android:textColor="@color/white"
android:text="西安" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="16°C"
android:textSize="24dp"
android:textColor="@color/white"
android:id="@+id/city_tem"/>


</LinearLayout>

</LinearLayout>

最终从数据库拿到信息呈现出的效果:
result

适配器AddCityAdapter

界面布局设计完成接下来就是适配器AddCityAdapter
public class AddCityAdapter extends RecyclerView.Adapter<AddCityAdapter.AddViewHolder>

适配器就是决定在此城市管理页面的RecyclerView中展示哪个页面,跟之前一样,前几篇文章都有篇幅概括,基本的一些操作就是,
添加一个构造方法,

1
2
3
4
5
6
7
private Context mContext;
private List<CityBean> mCityBeans;

public AddCityAdapter(Context context, List<CityBean> cityBeans) {
mContext = context;
this.mCityBeans = cityBeans;
}

创建一个类class AddViewHolder extends RecyclerView.ViewHolder来绑定控件,此时在城市管理页面要求点击某个城市,需要传值到MainActivity,并获取指定点击城市的天气信息
所以适配器中添加一个点击事件,首先在适配器中添加接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public interface OnItemClickListener {

/**
* 当RecyclerView某个被点击的时候回调
* @param view 点击item的视图
* @param position 点击得到的数据,参数自定义
*/
public void onItemClick(View view, int position);

}

private OnItemClickListener onItemClickListener;

public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}

然后在适配器中的类AddViewHolder中绑定点击事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class AddViewHolder extends RecyclerView.ViewHolder {

TextView cityCity,cityTem;//城市名,温度

public AddViewHolder(@NonNull View itemView) {
super(itemView);
//绑定控件
cityCity=itemView.findViewById(R.id.city_city);
cityTem = itemView.findViewById(R.id.city_tem);
//绑定点击事件
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (onItemClickListener!=null) {
onItemClickListener.onItemClick(view,getLayoutPosition());
}
}
});
}
}

然后就是一如既往的重写三个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@NonNull
@Override
public AddViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
//绑定控件,哪个页面展示在RecyclerView中,R.layout.added_city
View view = LayoutInflater.from(mContext).inflate(R.layout.added_city, parent, false);
AddViewHolder cityViewHolder = new AddViewHolder(view);
return cityViewHolder;
}

@Override
public void onBindViewHolder(@NonNull AddViewHolder holder, int position) {
CityBean cityBean = mCityBeans.get(position);
//控件传值
holder.cityCity.setText(cityBean.getName());
holder.cityTem.setText(cityBean.getTem());
}

@Override
public int getItemCount() {
return (mCityBeans == null) ? 0 : mCityBeans.size();
}

适配器AddCityAdapter源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class AddCityAdapter extends RecyclerView.Adapter<AddCityAdapter.AddViewHolder>{

private Context mContext;
private List<CityBean> mCityBeans;

public AddCityAdapter(Context context, List<CityBean> cityBeans) {
mContext = context;
this.mCityBeans = cityBeans;
}

@NonNull
@Override
public AddViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.added_city, parent, false);
AddViewHolder cityViewHolder = new AddViewHolder(view);
return cityViewHolder;
}

@Override
public void onBindViewHolder(@NonNull AddViewHolder holder, int position) {
CityBean cityBean = mCityBeans.get(position);

holder.cityCity.setText(cityBean.getName());
holder.cityTem.setText(cityBean.getTem());
}

@Override
public int getItemCount() {
return (mCityBeans == null) ? 0 : mCityBeans.size();
}

class AddViewHolder extends RecyclerView.ViewHolder {

TextView cityCity,cityTem;

public AddViewHolder(@NonNull View itemView) {
super(itemView);

cityCity=itemView.findViewById(R.id.city_city);
cityTem = itemView.findViewById(R.id.city_tem);

//绑定点击事件
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (onItemClickListener!=null) {
onItemClickListener.onItemClick(view,getLayoutPosition());
}
}
});
}
}
/**
* 点击响应事件
*/
public interface OnItemClickListener {

/**
* 当RecyclerView某个被点击的时候回调
* @param view 点击item的视图
* @param position 点击得到的数据
*/
public void onItemClick(View view, int position);

}

private OnItemClickListener onItemClickListener;

public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
}

操作数据库

然后进行数据库工具的构建,要操作数据库,一般的方法得需要实体类吧,接着就是城市实体类的封装,当获取当前定位天气信息时,把此数据类保存到数据库
所以先构建城市实体类CityBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class CityBean {

private String name;//城市名称
private String[] area;//县/区
private String tem;//温度
private String updateTime;//更新时间

//构造方法自定义,主要看数据库操作和new时传值
public CityBean() {
}

public CityBean(String name) {
this.name = name;
}

public CityBean(String name, String tem, String updateTime) {
this.name = name;
this.tem = tem;
this.updateTime = updateTime;
}
//get和set、toString注释

}

db

接着就是数据库的CityDatabaseConfig,有数据库名,表名的信息,以防操作数据库写错名称,故写在此配置下:

1
2
3
4
5
6
7
public class CityDatabaseConfig {
//数据库的名字
public static final String DATABASE_NAME = "cityDb";
//表名
public static final String TABLE_NAME = "city_weather";
}

然后是CityDbHelper继承自SQLiteOpenHelper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class CityDbHelper extends SQLiteOpenHelper {

public CityDbHelper(Context context) {
//创建数据库CityDatabaseConfig.DATABASE_NAME
super(context, CityDatabaseConfig.DATABASE_NAME, null, 1);
}

@Override
public void onCreate(SQLiteDatabase db) {
//创建表,字段(cityName text,tem text,updateTime text)
String sql = "create table if not exists "+CityDatabaseConfig.TABLE_NAME+"(cityName text,tem text,updateTime text)";
db.execSQL(sql);
Log.i(CityDatabaseConfig.DATABASE_NAME,"created success!!");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

Log.i(CityDatabaseConfig.DATABASE_NAME,"updated success!!");
}

}

接着是数据库一些操作的工具,里面写一些能用到的方法,自行扩展即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
public class DBUtils {

private Context context;
private CityDbHelper helper;

/**
* 单例模式
* @param context
*/
public DBUtils(Context context) {
this.context = context;
helper = new CityDbHelper(context) {
};
}

/**
* 向表中插入
*
* @param
* @return
*/
public boolean insertData(String cityName,String tem,String updateTime) {
SQLiteDatabase db = helper.getReadableDatabase();
ContentValues values = new ContentValues();

values.put("cityName", cityName);
values.put("tem", tem);
values.put("updateTime", updateTime);
long insert = db.insert(CityDatabaseConfig.TABLE_NAME, null, values);
if (insert > 0) {
return true;
}
return false;
}

//根据城市名称更新数据
public boolean updateByName(String cityName,String tem,String updateTime){
SQLiteDatabase db = helper.getReadableDatabase();
ContentValues values = new ContentValues();

values.put("cityName", cityName);
values.put("tem", tem);
values.put("updateTime", updateTime);
long update = db.update(CityDatabaseConfig.TABLE_NAME,values,"cityName=?", new String[]{cityName});

if (update>0){
return true;
}return false;
}

/**
* 查询全部的信息
*
* @return
*/
public List<CityBean> getAllCity() {
List<CityBean> list = new ArrayList<>();
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select * from " + CityDatabaseConfig.TABLE_NAME, null);
if (cursor != null && cursor.getCount() > 0) {
while (cursor.moveToNext()) {

String cityName = cursor.getString(0);
String tem = cursor.getString(1);
String updateTime = cursor.getString(2);

CityBean cityBean = new CityBean(cityName,tem,updateTime);
list.add(cityBean);
}
}
return list;
}

/**
* 根据城市名查询一条信息
*/
public CityBean getCityByName(String cityName) {
CityBean cityBean = new CityBean();
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select * from " + CityDatabaseConfig.TABLE_NAME + " where cityName=?", new String[]{cityName});
if (cursor != null && cursor.getCount() > 0) {
while (cursor.moveToFirst()) {

String cityNameResult = cursor.getString(0);
cityBean.setName(cityName);
break;
}

}
return cityBean;

}

/**
* 根据城市名删除一条数据
* @param cityName
* @return
*/
public boolean delCityByName(String cityName){
SQLiteDatabase db = helper.getReadableDatabase();

long delRow = db.delete(CityDatabaseConfig.TABLE_NAME, "cityName=?", new String[]{cityName});
if (delRow > 0) {
return true;
}
return false;

}

}

跳转页面,展示数据

一些基本的方法写好后就是MainActivity.java文件,用Intent跳转到城市管理界面,注意先引入数据库操作工具

1
DBUtils dbUtils = new DBUtils(MainActivity.this);

设置右上角图标的点击事件,此小圆点图标绑定为ivMore
tub

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
	ivMore.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (null == dbUtils.getCityByName(tvCity.getText().toString()).getName()) {
//数据库不存在此城市就插入
dbUtils.insertData(tvCity.getText().toString(),tvTem.getText().toString(),tvTime.getText().toString());
}
else {
//存在就更新
dbUtils.updateByName(tvCity.getText().toString(),tvTem.getText().toString(),tvTime.getText().toString());
// dbUtils.insertData("北京","30°C","2022-06-19 21:23:35");测试用
// dbUtils.delCityByName("商丘");测试用
List<CityBean> list = dbUtils.getAllCity();
Log.d("getAllCity", "allList<CityBean>>>>》》》" + list.toString());

ToastUtil.showShortToast(MainActivity.this, "当前位置:" + tvCity.getText());
Intent intent = new Intent(MainActivity.this, CityManagerActivity.class);
Log.d("getNowCity", "nowCity<CityBean>>>>》》》" + tvCity.getText());
// 要求跳转页面传值回来,requestCode:200;
startActivityForResult(intent, 200);
}
}
});

跳转到城市管理页面,添加activity.java文件,命名CityManagerActivity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class CityManagerActivity extends AppCompatActivity {

private AddCityAdapter addCityAdapter;//适配器
private RecyclerView rvAddedCity;

private List<CityBean> cityDbBeanList;
DBUtils dbUtils= new DBUtils(CityManagerActivity.this);

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.city_manager);

initView();
}


public void initView() {

rvAddedCity = findViewById(R.id.city_manager);

cityDbBeanList = dbUtils.getAllCity();
Log.d("cityList",">>>>>数据库中的数据》》》》》》"+cityDbBeanList);
addCityAdapter = new AddCityAdapter(CityManagerActivity.this,cityDbBeanList);
rvAddedCity.setAdapter(addCityAdapter);
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
rvAddedCity.setLayoutManager(layoutManager);
//设置点击事件
addCityAdapter.setOnItemClickListener(new AddCityAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
// ToastUtil.showShotToast(CityManagerActivity.this, cityDbBeanList.get(position).getName());
Intent intent = new Intent(CityManagerActivity.this,MainActivity.class);
intent.putExtra("selectedCity",cityDbBeanList.get(position).getName());
//返回MainActivity页面并传值setResult(200,intent);设置resultCode:200;
setResult(200,intent);
finish();

// ToastUtil.showShotToast(CityManagerActivity.this,"finish");
}
});
}

}

然后既然是要求页面返回传值,MainActivity就要重写方法onActivityResult接受传来的值

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == 200 && resultCode == 200) {
//getString拿到传来的值
String dt = data.getExtras().getString("selectedCity");
tvCity.setText(dt);
nowCity=dt;
//重新从api根据传来的城市名称获取天气
getWeather(nowCity);
ToastUtil.showLongToast(this,dt+"天气更新😘~");
}
}

自此一部分功能就实现了,跳转城市管理页面,展示城市,指定城市展示天气信息,

数据库操作实现了:

  1. 拿到数据库所有城市信息、
  2. 插入一条数据、
  3. 根据城市名称更新数据、
  4. 删除一条数据待完成,,自行拓展,只要工具类删除方法写对,无非就是添加个事件调用删除方法

更精细化的搜索城市,主动根据搜索到的城市添加城市到数据库,后篇文章会逐渐完善,此篇文章主要是数据库的一些操作和适配器添加点击事件,订阅此栏可同步获取项目搭建过程😘😘

==源码地址==:Gitee