效果图:源码与教程在文章末尾
登录界面
展示应用的登录页面,包含用户名输入框、密码输入框、登录按钮、注册按钮以及记住密码复选框。背景为轮播图,展示商城的特色商品或活动海报,吸引用户注意。
商品列表界面
呈现商品列表页面,左侧为商品分类列表,以列表形式展示各类商品分类,如推荐、手机、电脑等,点击可切换不同分类商品。右侧为商品展示区域,使用网格布局展示商品图片、标题和价格等信息,商品图片清晰展示商品外观,标题简洁明了,价格突出显示。
商品详情界面
显示商品详情页面,顶部为商品图片,占据较大空间,以高清大图展示商品细节,吸引用户购买。下方依次为商品标题、商品详情描述、价格信息以及加入购物车按钮。商品详情描述详细介绍商品的特点、功能、材质等信息,帮助用户了解商品。
5.4 购物车界面
展示购物车页面,使用列表布局展示购物车中的商品信息,每行包括商品图片、标题、价格、数量、增加按钮、减少按钮和删除按钮。底部显示购物车中商品的总价,方便用户查看。
5.5 订单列表界面
呈现订单列表页面,同样采用列表布局,展示订单的详细信息,包括订单中的商品图片、标题、价格、数量、收货地址、联系方式以及订单状态。用户可长按订单进行删除操作,订单状态以不同颜色或标识区分,如待发货、已发货、已完成等。
使用SQLiteOpenHelper类实现数据库的创建
重写相应的方法,创建创建user_table用户数据表,全局使用的shareperfermence来存储用户名和密码,实现记住密码 的功能
package com.example.shoppingmall.db;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import androidx.annotation.Nullable;
import com.example.shoppingmall.entity.UserInfo;
public class UserDbHelper extends SQLiteOpenHelper {
private static UserDbHelper sHelper;
private static final String DB_NAME = "user.db"; //数据库名
private static final int VERSION = 1; //版本号
//必须实现其中一个构方法
public UserDbHelper(@Nullable Context context, @Nullable String name, @Nullable SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
//创建单例,供使用调用该类里面的的增删改查的方法
public synchronized static UserDbHelper getInstance(Context context) {
if (null == sHelper) {
sHelper = new UserDbHelper(context, DB_NAME, null, VERSION);
}
return sHelper;
}
@Override
public void onCreate(SQLiteDatabase db) {
//创建user_table表
db.execSQL("create table user_table(user_id integer primary key autoincrement, " +
"username text," + //用户名
"password text," + //密码
"nickname text" + //
")");
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
}
/**
* 登录
*/
/**
* 登录 根据用户名查找用户
*/
@SuppressLint("Range")
public UserInfo login(String username) {
//获取SQLiteDatabase实例
SQLiteDatabase db = getReadableDatabase();
UserInfo userInfo = null;
String sql = "select user_id,username,password,nickname from user_table where username=?";
String[] selectionArgs = {username};
Cursor cursor = db.rawQuery(sql, selectionArgs);
if (cursor.moveToNext()) {
int user_id = cursor.getInt(cursor.getColumnIndex("user_id"));
String name = cursor.getString(cursor.getColumnIndex("username"));
String password = cursor.getString(cursor.getColumnIndex("password"));
String nickname = cursor.getString(cursor.getColumnIndex("nickname"));
userInfo = new UserInfo(user_id, name, password, nickname);
}
cursor.close();
db.close();
return userInfo;
}
/**
* 注册
*/
public int register(String username, String password, String nickname) {
//获取SQLiteDatabase实例
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
//填充占位符
values.put("username", username);
values.put("password", password);
values.put("nickname", nickname);
String nullColumnHack = "values(null,?,?,?)";
//执行
int insert = (int) db.insert("user_table", nullColumnHack, values);
db.close();
return insert;
}
//TODO 在这里根据自己的业务需求,编写增删改查的方法,如下所示
/**
* 修改密码
*/
public int updatePwd(String username, String password) {
//获取SQLiteDatabase实例
SQLiteDatabase db = getWritableDatabase();
// 填充占位符
ContentValues values = new ContentValues();
values.put("password", password);
// 执行SQL
int update = db.update("user_table", values, " username=?", new String[]{username});
// 关闭数据库连接
db.close();
return update;
}
}
用户信息实体类
package com.example.shoppingmall.entity;
public class UserInfo {
private int user_id;
private String username;
private static UserInfo sUserInfo;
public int getUser_id() {
return user_id;
}
public void setUser_id(int user_id) {
this.user_id = user_id;
}
public String getNickname() {
return nickname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public static UserInfo getsUserInfo() {
return sUserInfo;
}
public static void setsUserInfo(UserInfo sUserInfo) {
UserInfo.sUserInfo = sUserInfo;
}
private String password;
private String nickname;
public static UserInfo getUserInfo() {
return sUserInfo;
}
public UserInfo(int user_id, String username, String password, String nickname) {
this.user_id = user_id;
this.username = username;
this.password = password;
this.nickname = nickname;
}
public static void setUserInfo(UserInfo userInfo) {
sUserInfo = userInfo;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
然后编写登录启动页后的布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat 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"
tools:context=".activity.LoginActivity">
<!-- 轮播图 ViewPager -->
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="200dp" />
<!-- 小圆点指示器 -->
<LinearLayout
android:id="@+id/layoutDots"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginTop="8dp">
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/my_light_primary"
tools:ignore="MissingConstraints" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="20dp"
android:layout_marginTop="50dp"
android:gravity="center"
android:text="登录"
android:textColor="#333"
android:textSize="20dp"
tools:ignore="MissingConstraints" />
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="26dp"
android:orientation="vertical">
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/login_et_bg">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:src="@drawable/baseline_account_box_24" />
<EditText
android:id="@+id/et_username"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="@null"
android:hint="请输入用户名"
android:paddingLeft="10dp"
android:textColor="#333"
android:textColorHint="#333"
android:textSize="14sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:background="@drawable/login_et_bg">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:src="@drawable/baseline_https_24" />
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:background="@null"
android:hint="请输入密码"
android:inputType="textPassword"
android:paddingLeft="10dp"
android:text=""
android:textColor="#333"
android:textColorHint="#333"
android:textSize="14sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:text="记住密码"
android:layout_marginTop="10dp" />
<TextView
android:id="@+id/register"
android:layout_width="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_marginTop="10dp"
android:text="没有账号?注册"
android:textColor="@color/my_light_primary"
android:textColorHint="@color/my_light_primary"
android:textSize="14sp"
android:layout_height="100dp"/>
</RelativeLayout>
<Button
android:id="@+id/login"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginTop="10dp"
android:background="@drawable/button_background"
android:text="登录"
android:textColor="#fff"
android:textSize="16sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:text="忘记密码?"
android:textColor="#333"
android:textSize="14sp" />
</androidx.appcompat.widget.LinearLayoutCompat>
</androidx.appcompat.widget.LinearLayoutCompat>
然后注册后的信息存到SQLite
数据库, 编写登录页面跳转逻辑与验证账号密码的关系,获取用户名和密码的输入值,与存入到数据库注册的进行对比,记住密码的复选框也是通过preferences
对象的getBoolean 方法来获取判断的。复选框中的checkBox 也有判断逻辑; 如果设置的用户信息在数据库中存在这,把相对应的数据放到文本框不清楚。
// 从数据库中查询用户信息
UserInfo login = UserDbHelper.getInstance(LoginActivity.this).login(username);
if (login != null) {
// 验证用户名和密码是否匹配
if (username.equals(login.getUsername()) && password.equals(login.getPassword())) {
// 保存用户的登录状态和信息到 SharedPreferences
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("is_login", checkBox.isChecked());
editor.putString("username", username);
editor.putString("password", password);
editor.apply();
// 设置用户信息
UserInfo.setUserInfo(login);
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
// 跳转到主界面
Intent intent = new Intent(this, MainActivity.class);
startActivity(intent);
finish();
} else {
Toast.makeText(this, "用户名或密码错误", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(this, "账号未注册", Toast.LENGTH_SHORT).show();
}
}
});
轮播图的效果
在主要登录界面中有布局文件指示器和viewpager组件
<!-- 轮播图 ViewPager -->
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="200dp" />
<!-- 小圆点指示器 -->
<LinearLayout
android:id="@+id/layoutDots"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center"
android:layout_marginTop="8dp">
</LinearLayout>
然后在主登录文件Java代码中初始化组件和 图片数组用于存储图片,加上PageAdapter
适配器来充分渲染数据;
private ViewPager viewPager;
// 用于显示小圆点指示器的 LinearLayout
private LinearLayout layoutDots;
// 轮播图的图片资源数组
private int[] images = {R.drawable.equipment, R.drawable.thefive, R.drawable.re_2, R.drawable.sport, R.drawable.jianguo};
// 小圆点指示器数组
还有用于定时自动轮播和获取当前索引的属性值的全局变量
// 用于定时任务的 Handler
private Handler handler = new Handler();
// 定时任务的 Runnable
private Runnable runnable;
// 当前显示的图片索引
private int currentPage = 0;
分别定义三个方法, 表示适配器、监听器、启动自动切换任务的方法
// 设置 ViewPager 的适配器和监听器
setupViewPager();
// 设置小圆点指示器
setupDots();
// 启动自动切换任务
startAutoSlide();
在setUpviewpager 方法中有页面滚动和页面选中时的回调方法
// 设置 ViewPager 的适配器和监听器
private void setupViewPager() {
viewPager.setAdapter(new SliderAdapter());
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
// 页面滚动时的回调方法
}
@Override
public void onPageSelected(int position) {
// 页面选中时的回调方法
currentPage = position;
updateDots(position);
}
@Override
public void onPageScrollStateChanged(int state) {
// 页面滚动状态改变时的回调方法
if (state == ViewPager.SCROLL_STATE_IDLE) {
// 当页面滚动停止时,启动自动切换
startAutoSlide();
} else {
// 当页面正在滚动或被拖动时,停止自动切换
stopAutoSlide();
}
}
});
}
再设置一下小圆点指示器的状态还有切换后图片与原点绑定数据监听后的状态
// 设置小圆点指示器
@SuppressLint("UseCompatLoadingForDrawables")
private void setupDots() {
dots = new ImageView[images.length];
for (int i = 0; i < dots.length; i++) {
dots[i] = new ImageView(this);
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.dot_inactive));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.setMargins(8, 0, 8, 0);
layoutDots.addView(dots[i], params);
}
updateDots(currentPage);
}
// 更新小圆点指示器的状态
@SuppressLint("UseCompatLoadingForDrawables")
private void updateDots(int position) {
for (int i = 0; i < dots.length; i++) {
dots[i].setImageDrawable(getResources().getDrawable(R.drawable.dot_inactive));
}
dots[position].setImageDrawable(getResources().getDrawable(R.drawable.dot_active));
}
启动启动切换图片的方法,new 了一个 Runnable
子线程, 重写了run方法,判断当前是不是最后一张图片图片,如果是 把索引设置为零,切换到第一张图片; 如果不是的话就索引加加, // 否则,切换到下一张图片
currentPage++;
通过viewPager的setCurrentItem方法来设置当前的索引图片;延迟1s执行;
核心代码:
// 启动自动切换任务
private void startAutoSlide() {
runnable = new Runnable() {
@Override
public void run() {
if (currentPage == images.length - 1) {
// 如果当前是最后一张图片,则跳转到第一张图片
currentPage = 0;
} else {
// 否则,切换到下一张图片
currentPage++;
}
// 设置 ViewPager 显示当前图片
viewPager.setCurrentItem(currentPage, true);
// 延迟1秒后再次执行此任务
handler.postDelayed(this, 1500);
}
};
// 延迟1秒后开始执行自动切换任务
handler.postDelayed(runnable, 1500);
}
// 停止自动切换任务
private void stopAutoSlide() {
handler.removeCallbacks(runnable);
}
@Override
protected void onDestroy() {
super.onDestroy();
// 在 Activity 销毁时停止自动切换任务,避免内存泄漏
stopAutoSlide();
}
// 自定义 ViewPager 的适配器
private class SliderAdapter extends PagerAdapter {
@Override
public int getCount() {
// 返回图片的数量
return images.length;
}
@Override
public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
// 判断 view 是否与 object 对应
return view == object;
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
// 创建一个新的 ImageView 并设置图片资源
ImageView imageView = new ImageView(LoginActivity.this);
imageView.setImageResource(images[position]);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
container.addView(imageView);
return imageView;
}
@Override
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
// 移除不再使用的 ImageView
container.removeView((ImageView) object);
}
}
}
-----------点点点……………………………………代码太多,省略RecyclerView.Adapter
、Fragment、订单、购物车等适配器与具体结合的逻辑
代码,记录此次大作业的感受,若有兴趣的小伙伴可以观看视频教程进行学习
目教程B站传送门-教程原地址
通过网盘分享的文件:购物商城app源码rar.rar
链接: https://pan.baidu.com/s/1jGYMPdYXDP9tWBmjQNjD_Q?pwd=ricd 提取码: ricd
–来自百度网盘超级会员v2的分享
记录作业美好时刻,如果本篇博客对你有帮助的话就点个赞再走吧
········
~~``````````~~~~😄