【小白做项目-03】
本文为爱吃章鱼的熊二原创,转载请说明
一、效果展示
![](https://i-blog.csdnimg.cn/blog_migrate/4a05c1c3a0687adfc6e31245d79fba43.png)
![](https://i-blog.csdnimg.cn/blog_migrate/d08cc4bd5cedbab957ff2282de0550ab.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bc8b85f6d380af7a8b1d9a719d9f5b2c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/2e56899930326e19efdfd3763ae209fb.png)
![](https://i-blog.csdnimg.cn/blog_migrate/33d83aa313c7bd4e544e25abbeb37f3c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bff469d74e327f3bfd297ab1e293c28f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/bff469d74e327f3bfd297ab1e293c28f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/fca3f8f9f50ac5866bb35deae70352e8.png)
![](https://i-blog.csdnimg.cn/blog_migrate/ab7ffcdf33a8c2464da87de44dd6aa01.png)
![](https://i-blog.csdnimg.cn/blog_migrate/5ecd7ed7f7da9758818e880648e5654e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7dc4cef8d48ee265a6c019604873bd81.png)
![](https://i-blog.csdnimg.cn/blog_migrate/cf8fa2c39a74c1b20e8861aaca4d27a5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/3ba826f061746b70672d74f86c73f9f1.png)
![](https://i-blog.csdnimg.cn/blog_migrate/edd70715a8645086fa236ebec04b385e.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4a4071f0249b5e7ba94451838a643cc6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/b9d20678cf4d4cd235bad39916a2d57c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/093d468afaf4ec2c04e7c098ae869d3e.png)
二、项目需求
- 设计一个淘宝类APP
- 包含登陆注册功能
- 能够上架商品
- 能够购买商品
- 能够查看订单信息
- 个人用户系统
- 更换用户头像,修改
三、项目分析
这个项目涉及到前端和后端两个部分,作为安卓开发人员,我们主要关心前端的操作部分
在后端为我们准备好接口的情况下,这个项目的难点已经被大大简化。
我们的设计思路也显得很简单
根据接口,登录注册界面就很简单了
商品界面我们用recyclerview加载刷新
个人信息界面用get方法获取并显示。
判断用户登陆状态,我们用sharedPreferences保存登陆状态
相册拍照上传头像方法我们先获得对应的图片路径,再将它上传上去
联网部分我们用okhttp简化我们的联网获取信息的操作
用fragment展示我们的四个界面
四、实战操作
1.项目框架搭建
1.1导入依赖
1.2 Activity
1.3 xml文件
2.注册与登陆功能
![](https://i-blog.csdnimg.cn/blog_migrate/eda8926a482a07f6d87e133b0e154776.png)
![](https://i-blog.csdnimg.cn/blog_migrate/1209ed1ec67bbf2b47a32c8b78f541d8.png)
注册和登陆功能只需要对接口发送信号即可
我们利用post方法上传,注意接口对我们传输数据形式的要求,这里我们以Json形式利用okhttppost信息,先建立一个内部类,包含要上传的账号和密码信息
下面是post的代码
new Thread(new Runnable() {
@Override
public void run() {
OkHttpClient client = new OkHttpClient();
Gson gson = new Gson();
user_register use = new user_register();
use.account = account;
use.password = password;
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
RequestBody requestBody = RequestBody.create(JSON, gson.toJson(use));
Request request = new Request.Builder()
.url("http://49.232.214.94/api/register")
.post(requestBody)
.build();
Response response = null;
try {
response = client.newCall(request).execute();
String responseData = response.body().string();
user user = gson.fromJson(responseData, com.example.lego.user.class);
msg = user.getMsg();
int code = user.getCode();
if (code == 200) {
finish();
Intent intent = new Intent(register.this, login.class);
startActivity(intent);
}
Looper.prepare();
Toast.makeText(register.this, msg, Toast.LENGTH_SHORT).show();
Looper.loop();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
在这部分中,我们利用gson将user转化成Json形式,在放入requestbody中进行post请求,如果post返回给我们的code是200,则表示我们注册成功,跳转到登陆界面。若不是200,则Toast显示返回给我们的信息以作提示。
同样的登陆部分采用的方法即可
3.主体界面fragment设置
按照程序设计思路,在MainActivity中我们设计了4个fragment,分别对应商城、我的商店、购物车、我四部分,我们在商城界面展示商品,在我的商店部分上架和修改商品信息,在购物车部分查看订单,在我部分进行个人用户信息的修改与查看。
新建4个fragment
由于fragment属于activity的碎片,无法独立存在,因此,需要在mainactivity中预留一块布局给fragment以供显示,红线框住的部分即为fragment布局所占的位置,即使mainactivity中的Relativelayout部分
在fragment下方,我们设置了四个图标,以供点击不同图标达到切换fragment的目的。
3.1 fragment切换与选中状态
![](https://i-blog.csdnimg.cn/blog_migrate/d08cc4bd5cedbab957ff2282de0550ab.png)
我们可以从这张图中发现,在我们选中商店对应的fragment时,下方对应的图标也变成亮色状态。也就是说,我们再点击下方四个图片的时候,fragment被切换了,同时对应的图片也变换了。下面我们来说一下实现原理
fragment切换
我们为图标设置一个点击事件,点击对应图标即可跳转到对应的fragment
跳转的代码如下
getSupportFragmentManager().beginTransaction().replace(R.id.fragment,centerFragment).commit();
选中状态图片变换
图片变换实际就是换了一张图片
首先找到所需的两张图片资源
新建drawable resource文件
在文件中使用selector类进行图片选择
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/center1" android:state_selected="true"/>
<item android:drawable="@drawable/center2" android:state_selected="false"/>
</selector>
放入两张图片,利用selcted状态来判断选择显示那张图片
在mainactivity中的代码如下
对应的每一个图标的点击事件,由此达到了切换fragment和判断fragment选中状态改变底部导航栏的目的
4.商店界面
商店界面是四个fragment之一
这部分主要注重与商品的展示与点击商品进入商品详情页进行购买
4.1 商品展示
该部分的内容与《小白做项目-02(仿知乎日报)》中的内容极为相似。唯一区别在与fragment和activity的不同。fragment在依托于Activity。而同一个fragment也可以被多个Activity重复调用。因此在fragment进行操作时要getActivity或者getContext,这是与直接使用Activity比较不同的地方。
在这一部分,我们利用get请求获取所有商品的信息(或者获取一部分商品的信息,之后利用刷新加载),展示部分我们选择以Recyclerview来进行展示。
这里我放上部分代码进行讲解
final Thread thread =new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(100);
final OkHttpClient okHttpClient = new OkHttpClient();
Gson gson = new Gson();
Request request = new Request.Builder()
.url("http://49.232.214.94/api/goods")
.build();
Response response = okHttpClient.newCall(request).execute();
String responseData = response.body().string();
final good good =gson.fromJson(responseData, com.example.lego.good.class);
final com.example.lego.good.DataBean dataBean =good.getData();
final List<com.example.lego.good.DataBean.GoodsBean> goodsBeanList = dataBean.getGoods();
list.clear();
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
for( i=0;i<40;i++)
{
Map<String,Object> map=new HashMap<>();
com.example.lego.good.DataBean.GoodsBean goodsBean =goodsBeanList.get(i);
String name=goodsBean.getName();
int id =goodsBean.getGood_id();
String url=goodsBean.getImg();
String price=goodsBean.getPrice();
map.put("id",id);
map.put("name",name);
map.put("price",price);
map.put("image",url);
list.add(map);
}
count=goodsBeanList.size();
goodAdapter =new GoodAdapter(CenterFragment.this,list);
recyclerView.setAdapter(goodAdapter);
recyclerView.setLayoutManager(new GridLayoutManager(getContext(),2));
}
});
这是fragment中的代码,可以看到与在Activity中写并无太大差别。无非是将在Activity中的this换成了getActivity。
例外一点值得注意的是,我这里采取了一排两行的展示方法,对应的代码部分是setlayoutManager
recyclerView.setLayoutManager(new GridLayoutManager(getContext(),2));
在这行代码中的第二参数为2。即我选择以一行两个item的形式来展示商品。Recyclerview的具体使用操作可以参考本专栏的第二个项目,有详细的说明。这里我们就不在赘述。最后该页面的效果如下
4.2 商品详情页与购买操作
在商品详情页中,需要一个类似与淘宝界面的商品详情页,既能够看见商品的情况,也可以选择购买商品
在这一夜中,商品详情部分我们只需要在点击商品进入商品详情页的时候,将对应的商品序号post发送到后端,后端就会返回给我们对应商品得到详情信息。我们需要做的就是把商品的信息展示出来即可。
该页下方得到购买栏部分。点击加减号会加减需要购买的商品数量。之后点击加入购物车,系统便会将信息post到后端接口进行购买操作
但是需要注意的几点:
如果是购买自己的商品是不可以的,这部分的逻辑后端已经为我们编写好了,因此我们只需要Toast后端得到返回信息提示即可
如果购买时商品库存不够了显然也是不能购买的。这里我直接这部分的代码奉上。就不做详细讲解了。
package com.example.lego;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.google.gson.Gson;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
class user_order{
int good_id;
int goods_count;
}
public class good_info extends AppCompatActivity {
ImageView back;
Button add;
Button subtract;
ImageView img;
TextView name;
TextView info;
TextView price;
TextView restNumber;
RelativeLayout buy;
TextView number;
private int num=1;
private int length;
int good_number;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_good_info);
SharedPreferences sharedPreferences=getSharedPreferences("user",MODE_PRIVATE);
back =findViewById(R.id.backToMain);
add=findViewById(R.id.info_add);
subtract=findViewById(R.id.info_subtract);
buy=findViewById(R.id.buy);
number=findViewById(R.id.buy_number);
restNumber=findViewById(R.id.number);
img=findViewById(R.id.info_image);
name=findViewById(R.id.info_name);
info=findViewById(R.id.info_info);
price=findViewById(R.id.info_price);
final String token =sharedPreferences.getString("token",String.valueOf(11));
number.setText(Integer.valueOf(num).toString());
Intent it2=getIntent();
final Bundle bd=it2.getExtras();
final String id=bd.getString("id");
final int Id=Integer.parseInt(id);
buy.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(length>10)
{
Toast.makeText(good_info.this,"商品价格过高,不支持购买",Toast.LENGTH_SHORT).show();
}
else{
Thread thread =new Thread(new Runnable() {
@Override
public void run() {
try{
OkHttpClient okHttpClient =new OkHttpClient();
user_order order =new user_order();
order.good_id=Id;
order.goods_count=num;
Gson gson =new Gson();
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
RequestBody requestBody =RequestBody.create(JSON,gson.toJson(order));
Request request = new Request.Builder()
.url("*************该部分接口不对外开放*******")
.addHeader("Authorization",token)
.post(requestBody)
.build();
Response response = null;
response = okHttpClient.newCall(request).execute();
String responseData = response.body().string();
com.example.lego.order order1 =gson.fromJson(responseData, com.example.lego.order.class);
int code=order1.getCode();
if(code ==200)
{
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(good_info.this,"已加入购物车",Toast.LENGTH_SHORT).show();
restNumber.setText(Integer.valueOf(good_number-num).toString());
}
});
}
else if(code==422)
{
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(good_info.this,"不能购买自己的商品",Toast.LENGTH_SHORT).show();
}
});
}
else
{
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(good_info.this,"商品存货不够了",Toast.LENGTH_SHORT).show();
}
});
}
} catch (IOException e) {
e.printStackTrace();
}
}
});
thread.start();}
}
});
subtract.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(num>1)
{
num--;
number.setText(Integer.valueOf(num).toString());
}
else
{
Toast.makeText(good_info.this,"最少购买一份商品",Toast.LENGTH_SHORT).show();
}
}
});
add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
num++;
number.setText(Integer.valueOf(num).toString());
}
});
back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
Thread thread =new Thread(new Runnable() {
@Override
public void run() {
try {
OkHttpClient okHttpClient =new OkHttpClient();
Gson gson =new Gson();
Request request = new Request.Builder()
.url("url"+id)
.build();
Response response = okHttpClient.newCall(request).execute();
String responseData = response.body().string();
final new_detail detail =gson.fromJson(responseData, com.example.lego.new_detail.class);
runOnUiThread(new Runnable() {
@Override
public void run() {
new_detail.DataBean dataBean = detail.getData();
new_detail.DataBean.GoodBean goodBean=dataBean.getGood();
String good_name=goodBean.getName();
String good_price=goodBean.getPrice();
length =good_price.length();
String good_info =goodBean.getInfo();
String good_url =goodBean.getImg();
good_number=goodBean.getQuantity();
restNumber.setText("剩余数量:"+good_number);
name.setText(good_name);
price.setText(good_price);
info.setText(good_info);
Glide.with(com.example.lego.good_info.this).load(good_url).into(img);
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
});
thread.start();
}
}
5.我的店铺
![](https://i-blog.csdnimg.cn/blog_migrate/bc8b85f6d380af7a8b1d9a719d9f5b2c.png)
在这部分当中,很明显,第一步,我们需要获取我们已经创建的商品信息。第二步,我们能够修改我们已经上架的商品。第三步,我们可以新创建商品的信息。这就涉及到了三个接口的使用。
5.1获取我的商品信息
在这部分中,需要特别获得我的商品,根据后端写得接口。我们需要在get网络请求中的请求头中带上识别用户的token。代码如下:
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
try{
Thread.sleep(100);
OkHttpClient okHttpClient = new OkHttpClient();
Gson gson = new Gson();
String token =sharedPreferences.getString("token",String.valueOf(123));
Request request = new Request.Builder()
.addHeader("Authorization",token)
.url("************该接口不对外开放******")
.build();
Response response = okHttpClient.newCall(request).execute();
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getContext(),"没有更多数据了",Toast.LENGTH_SHORT).show();
}
});
} catch (Exception e) {
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
list.clear();
Map<String,Object> map=new HashMap<>();
map.put("1",1);
map.put("2",2);
list.add(map);
StoreAdapter storeAdapter =new StoreAdapter(StoreFragment.this,list);
recyclerView.setAdapter(storeAdapter);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
}
});
e.printStackTrace();
}
}
});
thread.start();
5.2 创建商品调用相机相册获得图片
创建商品这部分,和注册用户部分相同,只需要向后端接口我们要上传的商品信息即可。但难点在于图片的选取。如何获取图片的地址并上传到后端呢?这是我们真正要解决的问题所在。
上传图片的关键在于图片绝对路径的获取。我们都知道,有了手机里的绝对路径,我们就可以根据绝对路径将图片上传到服务器。获取绝对路径,我们首先要选定图片 ,这就需要调取相册或者相机。在这里我将代码附上并讲解
第一步,相机相册的调用
private void selectCamera() {
//创建file对象,用于存储拍照后的图片,这也是拍照成功后的照片路径
outputImage = new File(this.getExternalCacheDir(), "camera_photos.jpg");
try {
//判断文件是否存在,存在删除,不存在创建
if (outputImage.exists()) {
outputImage.delete();
}
outputImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
photoUri = Uri.fromFile(outputImage);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
startActivityForResult(intent, TAKE_PHOTO);
}
public static final String STR_IMAGE = "image/*";
//选择相册
private void selectPhoto(){
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, STR_IMAGE);
startActivityForResult(intent, GET_BACKGROUND_FROM_CAPTURE_RESOULT);
}
第二步 获取绝对路径
public static String getRealPathFromUri(Context context, Uri uri) {
if(context == null || uri == null) {
return null;
}
if("file".equalsIgnoreCase(uri.getScheme())) {
return getRealPathFromUri_Byfile(context,uri);
} else if("content".equalsIgnoreCase(uri.getScheme())) {
return getRealPathFromUri_Api11To18(context,uri);
}
// int sdkVersion = Build.VERSION.SDK_INT;
// if (sdkVersion < 11) {
// // SDK < Api11
// return getRealPathFromUri_BelowApi11(context, uri);
// }
// // SDK > 19
return getRealPathFromUri_AboveApi19(context, uri);//没用到
}
private static String getRealPathFromUri_Byfile(Context context,Uri uri){
String uri2Str = uri.toString();
String filePath = uri2Str.substring(uri2Str.indexOf(":") + 3);
return filePath;
}
@SuppressLint("NewApi")
private static String getRealPathFromUri_AboveApi19(Context context, Uri uri) {
String filePath = null;
String wholeID = null;
wholeID = DocumentsContract.getDocumentId(uri);
// 使用':'分割
String id = wholeID.split(":")[1];
String[] projection = { MediaStore.Images.Media.DATA };
String selection = MediaStore.Images.Media._ID + "=?";
String[] selectionArgs = { id };
Cursor cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection,
selection, selectionArgs, null);
int columnIndex = cursor.getColumnIndex(projection[0]);
if (cursor.moveToFirst()) {
filePath = cursor.getString(columnIndex);
}
cursor.close();
return filePath;
}
第三步 裁剪获取的图片
public void cropRawPhoto(Uri uri) {
//创建file文件,用于存储剪裁后的照片
File cropImage = new File(Environment.getExternalStorageDirectory(), "crop_image.jpg");
String path = cropImage.getAbsolutePath();
try {
if (cropImage.exists()) {
cropImage.delete();
}
cropImage.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
cropImgUri = Uri.fromFile(cropImage);
Intent intent = new Intent("com.android.camera.action.CROP");
//设置源地址uri
intent.setDataAndType(photoUri, "image/*");
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 200);
intent.putExtra("outputY", 200);
intent.putExtra("scale", true);
//设置目的地址uri
intent.putExtra(MediaStore.EXTRA_OUTPUT, cropImgUri);
//设置图片格式
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("return-data", false);
intent.putExtra("noFaceDetection", true); // no face detection
startActivityForResult(intent, RESULT_REQUEST_CODE);
}
5.3 AlerDialog的使用
当我点击上传图片时,会弹出一个小的对话框选择相册或者拍照。这种小对话框我们可以使用AlerDialog表示
final String sex[]=new String[]{" 相册"," 取消"};
AlertDialog.Builder builder1 =new AlertDialog.Builder(Create_good.this);
builder1.setIcon(R.drawable.ic_launcher_foreground);
builder1.setTitle("选择:");
builder1.setItems(sex, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if(which==1)
{
}
else
{
selectPhoto();
final String hash=sharedPreferences.getString("hash", String.valueOf(111));
Log.v("tag",hash);
final Thread thread =new Thread(new Runnable() {
@Override
public void run() {
try{
do {
Thread.sleep(1000);
final SharedPreferences sharedPreferences = getSharedPreferences("user", MODE_PRIVATE);
image_hash =sharedPreferences.getString("hash",String.valueOf(111));
Log.v("tag",image_hash);
}
while (image_hash.equals(hash));
runOnUiThread(new Runnable() {
@Override
public void run() {
Glide.with(Create_good.this).load(image_hash).into(img);
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
});
thread.start();
}
}
});
builder1.create().show();
5.4 创建商品
创建商品时我们只需将商品信息post到后端即可
方法与注册大同小异,代码就不放了。大家可以尝试自己写一下。
5.5 修改商品信息
修改商品和创建商品最本质的区别在于与后端接口方式的不同。修改商品我们所用的方法为put。
这是因为虽然post和put都可以用于修改和创建信息。但是post的每一次都相当于创建一次新的信息,而不会覆盖原来的信息。而put请求是可以覆盖掉原来的信息。因此我们用put方法修改商品信息。
代码如下
final String name =get_name.getText().toString();
String jiage=get_price.getText().toString();
String shuliang=get_quantity.getText().toString();
final String info =get_quantity.getText().toString();
if(name==null || name.length()==0)
{
Toast.makeText(Create_good.this,"商品未命名",Toast.LENGTH_SHORT).show();
}
else if(jiage==null || jiage.length()==0)
{
Toast.makeText(Create_good.this,"价格不能为空",Toast.LENGTH_SHORT).show();
}
else if(shuliang==null || shuliang.length()==0)
{
Toast.makeText(Create_good.this,"数量不能为空",Toast.LENGTH_SHORT).show();
}
else if(Integer.parseInt(shuliang)>99)
{
Toast.makeText(Create_good.this,"商品数量不能超过99",Toast.LENGTH_SHORT).show();
}
else if (Integer.parseInt(jiage)>10000000)
{
Toast.makeText(Create_good.this,"商品价格不合理",Toast.LENGTH_SHORT).show();
}
else
{
Thread thread =new Thread(new Runnable() {
@Override
public void run() {
try{
final int price=Integer.parseInt(get_price.getText().toString());
final int quantity =Integer.parseInt(get_quantity.getText().toString());
String hash=sharedPreferences.getString("hash",null);
OkHttpClient client = new OkHttpClient();
Gson gson = new Gson();
good_create good=new good_create();
good.name=name;
good.price=price;
good.quantity=quantity;
good.info=info;
if(hash==null)
{
good.img=null;
}
else
{
good.img=hash;
}
MediaType JSON = MediaType.parse("application/json; charset=utf-8");
String token=sharedPreferences.getString("token",null);
RequestBody requestBody =RequestBody.create(JSON,gson.toJson(good));
Request request = new Request.Builder()
.addHeader("Authorization",token)
.url("http://49.232.214.94/api/good")
.post(requestBody)
.build();
Response response = null;
response = client.newCall(request).execute();
String responseData = response.body().string();
good_creat good_creat=gson.fromJson(responseData, com.example.lego.good_creat.class);
String msg=good_creat.getMsg();
Looper.prepare();
Toast.makeText(Create_good.this,msg,Toast.LENGTH_SHORT).show();
Looper.loop();
} catch (IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(Create_good.this,"网络连接已断开",Toast.LENGTH_SHORT).show();
}
});
e.printStackTrace();
}
}
});
thread.start();
finish();}
6.购物车
购物车部分类似与商城部分,只需展示自己已经购买的商品,从接口后端返回即可。效果如图
7.用户系统(sharedPreferences)判断登录状态
个人信息部分同样是从后端获取信息并显示,如需修改只需把修改后的信息put上去即可。
这里讲解sharedPreferences的用法
sharedPreferences相当于内置于APP中存储信息轻量级文件使用。
使用方法也非常简单
第一步 建立
SharedPreferences sharedPreferences=getActivity().getSharedPreferences("user", Context.MODE_PRIVATE);
第二步 写入信息
SharedPreferences.Editor editor=sharedPreferences.edit();
editor.putBoolean("islogin",false);
editor.commit();
第三步 读取信息
String islogin=sharedPreferences.getBoolean("islogin",false);
这三部分可以位于app中的不同Activity。我们判断登录状态时,如果用户登录,我们设置islogin参数为真。在下次启动app时在欢迎界面读取sharedPreferences中的islogin信息,如果为真,则跳过登陆部分。反之则需要登陆。
8.异常处理
同样,由于该软件需要联网。我们也要为app设置断网处理。具体操作可以在我的第二个项目(仿知乎日报)中查看,这里不做赘述
小白做项目-02
为了提高用户体验,我还做了一些提示处理
思路与断网处理都是一样的
9. 总结
该项目具有一定难度,非常能够提升Android开发的技术,尤其是全局考虑,设计软件思维等方面的能力。当然关于该项目,由于是前后端相接,而这里只讲解了前段部分的内容,后端接口是作者学长已经写好的部分,所以在这里感谢学长!
当然,本篇教程还存在许多不足,关于安卓生命周期,thread线程跳跃等部分我没有讲解。是因为这些问题是在真正做项目遇到的难点,也就是真正去做了才会思考和想办法解决。如果你是试着去做类似项目,遇到了这样的问题,我可以为你提供我的思路!私信我即可。
由于一些原因,接口部分不能透露。
如果想要源码的可以私信我,仅供学习使用