Android Studio初学者实例:仿拼多多砍价页面
本次实验较为综合,主要是用到了RecyclerView、okhttp库(用于网络访问)、gson库(解析json数据)、tomacat(服务器存放图片、文字等),所以代码较多,但知识点常用,如果毕设、工作接触安卓可以深入学习。我也会在本章深入讲解
首先列表的数据都是来源于Tomcat,所以在这里引用一下别人Tomcat的安装使用办法,在后面代码中我的Tomcat目录为:D:\Tomcat\apache-tomcat-8.5.89,大家也可装到D盘。
Tomcat安装步骤及详细配置教程(2022最新版)_tomcat安装及配置教程_Java程序员-张凯的博客-CSDN博客
添加文件到D:\Tomcat\apache-tomcat-8.5.89\webapps\ROOT目录下,
这里添加了两个文件:goods文件夹、goods_list_data.json(文件后续我可能会发布到资源里包括源码),一个json文件以及一个包含了图片、json的文件夹
给一下goods_list_data.json的内容:请把XXXX改成本机IP地址(cmd中ipconfig可以查看)
[ {"id":1,"count":"5.4万","goodsName":"富士拍立得相机","goodsPic":"http://XXXX:8080/goods/img/polaroid.png"}, {"id":2,"count":"5.3万","goodsName":"格兰仕微波炉","goodsPic":"http://XXXX:8080/goods/img/microwave_oven.png"}, {"id":3,"count":"1.4万","goodsName":"新国标电动车","goodsPic":"http://XXXX:8080/goods/img/electric_vehicle.png"}, {"id":4,"count":"1.6万","goodsName":"官方订制投影仪","goodsPic":"http://XXXX:8080/goods/img/projector.png"}, {"id":5,"count":"0.4万","goodsName":"美的35L烤箱","goodsPic":"http://XXXX:8080/goods/img/oven.png"}, {"id":6,"count":"3.3万","goodsName":"儿童学习桌","goodsPic":"http://XXXX:8080/goods/img/learning_table.png"} ]
在D:\Tomcat\apache-tomcat-8.5.89\bin目录下找到startup.bat双击,即运行tomcat,出现一个弹窗,请勿关闭,否则访问不到,为了测试tomcat是否正确开启,可以本地访问
http://localhost:8080/出现页面则代表启动成功
准备Android Studio一些配置
在AndroidManifest.xml文件中添加语句:声明网络权限
此外高版本的android还需要完成网络安全访问配置
在res文件夹下创建xml文件夹,并新建文件network_config.xml
内容为:
并还需要返回到AndroidManifest.xml,添加一句话
android:networkSecurityConfig="@xml/network_config"
在build.gradle文件中添加第三方库(app下的build.gradle)
在dependencies {}花括号中添加如下代码:
//okhttp implementation("com.squareup.okhttp3:okhttp:4.10.0") //gson implementation 'com.google.code.gson:gson:2.6.2' //glide implementation 'com.github.bumptech.glide:glide:4.4.0'
OK,准备环节结束,下面来看看效果图:
首先该页面的XML布局代码,可以看到页面很明显的有一个两列的列表,所以肯定是无法使用ListView的(难道你要和我杠,用两个ListView横向排列~~~),所以我们肯定采用能横向显示、能竖向显示、能瀑布流、能多行等等的RecyclerView,在高版本中RecyclerView已经无需添加依赖什么的,所以直接打RecyclerView就出现androidx.recyclerview.widget.RecyclerView选择即可
activity_main69.xml(名字不一定和我一样)
要是你是老版本的,请注意导入RecyclerView,
在build.gradle文件中的dependencies闭包中添加指令
implementation 'com.android.support:recyclerview-v7:28.0.0-alpha1'
然后还有一个XML文件,因为使用到了RecyclerView列表控件,所以还需要一个item界面,布局和控件就不说了,线性布局、ImageView、TextView等控件。
goods_item.xml
另外显然是一个较全的小项目,所以需要面向对象编程,在本次实例中有一个对象:砍价商品,然后包含了商品id、砍价数量、商品名称、商品图片,然后使用alt+insert快速构造构造方法、get方法、set方法。
GoodsInfo
public class GoodsInfo { private int id; // id private String count; // 已砍数 private String goodsName; // 名称 private String goodsPic; // 图片 public int getId() { return id; } public void setId(int id) { this.id = id; } public String getCount() { return count; } public void setCount(String count) { this.count = count; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } public String getGoodsPic() { return goodsPic; } public void setGoodsPic(String goodsPic) { this.goodsPic = goodsPic; } public GoodsInfo(int id, String count, String goodsName, String goodsPic) { this.id = id; this.count = count; this.goodsName = goodsName; this.goodsPic = goodsPic; } }
接下来是列表RecyclerView适配器
GoodsAdapter
import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import java.util.ArrayList; import java.util.List; public class GoodsAdapter extends RecyclerView.Adapter { private Context mContext; private List GoodsList = new ArrayList(); //构造方法 传递获取的上下文 public GoodsAdapter(Context context) { this.mContext = context; } //设置数据 并更新列表 public void setData(List GoodsList) { this.GoodsList = GoodsList; notifyDataSetChanged(); } //加载布局文件 LayoutInflater.from(mContext).inflate //可以类比一下ListView中 View view = inflater.inflate这个方法 也是加载布局文件(其实是一样的) @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { //获取加载item界面文件 这里是R.layout.goods_item View view = LayoutInflater.from(mContext).inflate(R.layout.goods_item, parent, false); //new 一个viewholder 到时候复用用 RecyclerView.ViewHolder holder = new MyViewHolder(view); return holder; } // 数据绑定,将数据绑定到视图上 @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { GoodsInfo bean = GoodsList.get(position); // 将已砍的商品数量和商品名称设置到界面控件上 ((MyViewHolder) holder).tv_count.setText("已砍" + bean.getCount() + "件"); ((MyViewHolder) holder).tv_goods_name.setText(bean.getGoodsName()); // 将商品图片数据设置到图片控件iv_img上 Glide.with(mContext) .load(bean.getGoodsPic()) // .error(R.mipmap.ic_launcher) .into(((MyViewHolder) holder).iv_img); } // 获取条目总数 @Override public int getItemCount() { return GoodsList.size(); } //zi定义的viewhlder class MyViewHolder extends RecyclerView.ViewHolder { TextView tv_count, tv_goods_name; ImageView iv_img; Button btn_free; public MyViewHolder(View view) { super(view); tv_count = view.findViewById(R.id.tv_count); tv_goods_name = view.findViewById(R.id.tv_goods_name); iv_img = view.findViewById(R.id.iv_img); btn_free = view.findViewById(R.id.btn_free); } } }
另外 最主要的逻辑代码
MainActivity69
import androidx.appcompat.app.AppCompatActivity; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.os.Bundle; import android.os.Bundle; import android.os.Handler; import android.os.Message; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import java.io.IOException; import java.lang.reflect.Type; import java.util.List; import okhttp3.Call; import okhttp3.Callback; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; public class MainActivity69 extends AppCompatActivity { private GoodsAdapter adapter; // 列表的适配器 public static final int MSG_GOODS_OK = 1; // 获取数据 private MHandler mHandler; // 内网接口 public static final String WEB_SITE = "http://192.168.128.1:8080/goods"; // 商品列表接口 public static final String REQUEST_GOODS_URL = "/goods_list_data.json"; private RecyclerView rv_list; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main69); mHandler = new MHandler(); init(); initData(); } private void init() { rv_list = findViewById(R.id.rv_list); //设置上下文以及 RecyclerView列数 GridLayoutManager manager = new GridLayoutManager(this, 2); // 将manager对象设置到控件rv_list上 rv_list.setLayoutManager(manager); adapter = new GoodsAdapter(MainActivity69.this); // 将数据适配器的对象adapter设置到控件rv_list上 rv_list.setAdapter(adapter); } private void initData() { //okhttp OkHttpClient okHttpClient = new OkHttpClient(); Request request = new Request.Builder().url(WEB_SITE + REQUEST_GOODS_URL).build(); Call call = okHttpClient.newCall(request); // 开启异步线程访问网络,从服务器上获取商品列表的数据 //okhttp不需要 new新线程 call.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) throws IOException { String res = response.body().string(); // 获取商品数据 Message msg = new Message(); msg.what = MSG_GOODS_OK; msg.obj = res; mHandler.sendMessage(msg); } @Override public void onFailure(Call call, IOException e) { } }); } /** * 事件捕获 */ class MHandler extends Handler { @Override public void dispatchMessage(Message msg) { super.dispatchMessage(msg); switch (msg.what) { case MSG_GOODS_OK: if (msg.obj != null) { // 获取传递过来的JSON数据vlResult String vlResult = (String) msg.obj; // 解析获取的JSON数据vlResult,并将解析后的数据存放在集合goodsInfos中 List goodsInfos = getGoodsList(vlResult); // 将集合goodsInfos设置到数据适配器的对象adapter中 adapter.setData(goodsInfos); } break; } } } public List getGoodsList(String json) { Gson gson = new Gson(); // 使用gson库解析JSON数据 // 创建一个TypeToken的匿名子类对象,并调用对象的getType()方法 Type listType = new TypeToken() { }.getType(); // 把获取到的集合数据存放到goodsInfos中 List goodsInfos = gson.fromJson(json, listType); return goodsInfos; } }
那么要是出现bug:
1.图片不显示或标题不显示,ID显示出来了:JSON文件的Key必须和实体类中的属性一样
2.运行不起来:检查一下Tomcat是否启动,注意以下图片运行端口界面不能关闭,这就是Tomcat正在运行