You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中基于PHP+MySQL实现ListView点击跳转显示关联产品列表咨询

嘿,这个需求我熟!咱们一步步来实现,从数据库到PHP接口再到Android端,全流程给你捋清楚~

实现步骤详解

1. 数据库层面准备

首先得确保你的数据库里有两张关联表,对应你的两级列表需求:

  • 分类表(比如categories):存储第一个ListView的项,字段包括 id(主键)、name(分类名称)、image_url(分类图片地址)
  • 产品表(比如products):存储关联产品,字段包括 id(主键)、category_id(关联分类表的id)、product_name(产品名)、product_image(产品图)、price(价格)

这样点击某个分类时,就能通过category_id筛选出对应的所有产品。

2. 编写PHP接口

需要两个接口:一个获取分类列表,另一个根据分类ID拉取关联产品。

接口1:获取分类列表(get_categories.php)

<?php
// 替换成你的数据库连接信息
$conn = mysqli_connect("localhost", "db_username", "db_password", "db_name");
if (!$conn) {
    die("连接失败: " . mysqli_connect_error());
}

$sql = "SELECT * FROM categories";
$result = mysqli_query($conn, $sql);

$categories = [];
if (mysqli_num_rows($result) > 0) {
    while($row = mysqli_fetch_assoc($result)) {
        $categories[] = $row;
    }
}
// 返回JSON格式数据,方便Android解析
echo json_encode($categories);
mysqli_close($conn);
?>

接口2:根据分类ID获取产品(get_products.php)

这个接口需要接收前端传的category_id参数,一定要用预处理语句防止SQL注入:

<?php
$conn = mysqli_connect("localhost", "db_username", "db_password", "db_name");
if (!$conn) {
    die("连接失败: " . mysqli_connect_error());
}

// 获取前端传递的分类ID
$category_id = $_GET['category_id'];
// 预处理SQL语句
$sql = "SELECT * FROM products WHERE category_id = ?";
$stmt = mysqli_prepare($conn, $sql);
mysqli_stmt_bind_param($stmt, "i", $category_id);
mysqli_stmt_execute($stmt);
$result = mysqli_stmt_get_result($stmt);

$products = [];
if (mysqli_num_rows($result) > 0) {
    while($row = mysqli_fetch_assoc($result)) {
        $products[] = $row;
    }
}
echo json_encode($products);
mysqli_close($conn);
?>

3. Android端实现

第一步:配置权限和依赖

在AndroidManifest.xml中添加网络权限:

<uses-permission android:name="android.permission.INTERNET" />

如果用Retrofit做网络请求(比原生HttpURLConnection更方便),在build.gradle(Module level)中添加依赖:

implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.github.bumptech.glide:glide:4.12.0' // 用于加载网络图片

第二步:创建数据模型类

对应数据库的表结构,创建两个实体类:

  • Category.java(分类数据):
public class Category {
    private int id;
    private String name;
    private String image_url;

    // Getter和Setter方法
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public String getImage_url() { return image_url; }
    public void setImage_url(String image_url) { this.image_url = image_url; }
}
  • Product.java(产品数据):
public class Product {
    private int id;
    private String product_name;
    private String product_image;
    private double price;

    // Getter和Setter方法
    public int getId() { return id; }
    public void setId(int id) { this.id = id; }
    public String getProduct_name() { return product_name; }
    public void setProduct_name(String product_name) { this.product_name = product_name; }
    public String getProduct_image() { return product_image; }
    public void setProduct_image(String product_image) { this.product_image = product_image; }
    public double getPrice() { return price; }
    public void setPrice(double price) { this.price = price; }
}

第三步:创建Retrofit接口

定义网络请求的接口方法:

public interface ApiService {
    @GET("get_categories.php")
    Call<List<Category>> getCategories();

    @GET("get_products.php")
    Call<List<Product>> getProducts(@Query("category_id") int categoryId);
}

第四步:实现第一个ListView(分类列表)

在MainActivity中加载分类数据,并处理点击跳转逻辑:

public class MainActivity extends AppCompatActivity {
    private ListView categoryListView;
    private List<Category> categoryList;
    private CategoryAdapter adapter;

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

        categoryListView = findViewById(R.id.category_list);
        categoryList = new ArrayList<>();
        adapter = new CategoryAdapter(this, categoryList);
        categoryListView.setAdapter(adapter);

        // 初始化Retrofit,替换成你的服务器接口根地址
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://你的服务器IP/项目目录/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        ApiService apiService = retrofit.create(ApiService.class);

        // 请求分类数据
        Call<List<Category>> call = apiService.getCategories();
        call.enqueue(new Callback<List<Category>>() {
            @Override
            public void onResponse(Call<List<Category>> call, Response<List<Category>> response) {
                if (response.isSuccessful() && response.body() != null) {
                    categoryList.addAll(response.body());
                    adapter.notifyDataSetChanged();
                }
            }

            @Override
            public void onFailure(Call<List<Category>> call, Throwable t) {
                Toast.makeText(MainActivity.this, "分类加载失败: " + t.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });

        // 点击分类项跳转到产品列表页
        categoryListView.setOnItemClickListener((parent, view, position, id) -> {
            Category selectedCategory = categoryList.get(position);
            Intent intent = new Intent(MainActivity.this, ProductsActivity.class);
            intent.putExtra("CATEGORY_ID", selectedCategory.getId());
            startActivity(intent);
        });
    }
}

然后编写分类列表的适配器CategoryAdapter.java:

public class CategoryAdapter extends ArrayAdapter<Category> {
    private Context context;
    private List<Category> list;

    public CategoryAdapter(Context context, List<Category> list) {
        super(context, 0, list);
        this.context = context;
        this.list = list;
    }

    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        View listItem = convertView == null ? LayoutInflater.from(context).inflate(R.layout.category_list_item, parent, false) : convertView;

        Category currentCategory = list.get(position);

        TextView nameTV = listItem.findViewById(R.id.category_name);
        ImageView imageIV = listItem.findViewById(R.id.category_image);

        nameTV.setText(currentCategory.getName());
        // 用Glide加载网络图片
        Glide.with(context).load(currentCategory.getImage_url()).into(imageIV);

        return listItem;
    }
}

对应的category_list_item.xml布局(可根据需求调整样式):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    android:orientation="horizontal">

    <ImageView
        android:id="@+id/category_image"
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:scaleType="centerCrop"/>

    <TextView
        android:id="@+id/category_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_marginStart="16dp"
        android:textSize="18sp"/>
</LinearLayout>

第五步:实现产品列表页面(ProductsActivity)

接收传递的分类ID,请求并展示关联产品:

public class ProductsActivity extends AppCompatActivity {
    private ListView productListView;
    private List<Product> productList;
    private ProductAdapter adapter;
    private int categoryId;

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

        productListView = findViewById(R.id.product_list);
        productList = new ArrayList<>();
        adapter = new ProductAdapter(this, productList);
        productListView.setAdapter(adapter);

        // 获取传递的分类ID
        categoryId = getIntent().getIntExtra("CATEGORY_ID", -1);
        if (categoryId == -1) {
            Toast.makeText(this, "无效的分类ID", Toast.LENGTH_SHORT).show();
            finish();
            return;
        }

        // 请求产品数据
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("http://你的服务器IP/项目目录/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        ApiService apiService = retrofit.create(ApiService.class);

        Call<List<Product>> call = apiService.getProducts(categoryId);
        call.enqueue(new Callback<List<Product>>() {
            @Override
            public void onResponse(Call<List<Product>> call, Response<List<Product>> response) {
                if (response.isSuccessful() && response.body() != null) {
                    productList.addAll(response.body());
                    adapter.notifyDataSetChanged();
                }
            }

            @Override
            public void onFailure(Call<List<Product>> call, Throwable t) {
                Toast.makeText(ProductsActivity.this, "产品加载失败: " + t.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }
}

产品适配器ProductAdapter.java和对应的product_list_item.xml布局逻辑和分类适配器一致,只需替换为产品的字段即可,这里就不重复赘述了。

关键注意事项

  • 网络请求线程:上面用了Retrofit的异步请求,避免了主线程阻塞;如果用原生请求,一定要手动开启子线程。
  • 服务器地址:Android模拟器中不能用localhost,要替换成10.0.2.2;真机测试需确保手机和服务器在同一局域网,使用服务器的局域网IP。
  • 图片加载:用Glide/Picasso这类库,自带缓存和线程处理,比自己写Bitmap加载更稳定。
  • 安全问题:PHP接口必须用预处理语句防止SQL注入,不要直接拼接SQL字符串。

内容的提问来源于stack exchange,提问作者akash

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:07:39