Compare commits
39 Commits
| Author | SHA1 | Date |
|---|---|---|
|
|
ae7cb082c1 | 3 weeks ago |
|
|
4c7a4236c0 | 2 months ago |
|
|
d5fe0481ac | 2 months ago |
|
|
d86c0e6c70 | 2 months ago |
|
|
5b3b5c1923 | 2 months ago |
|
|
be3945fb1e | 2 months ago |
|
|
fc2ab52293 | 2 months ago |
|
|
8acf678eba | 2 months ago |
|
|
0aa8412c57 | 2 months ago |
|
|
a9de196957 | 2 months ago |
|
|
ab66af1810 | 2 months ago |
|
|
582e64efb4 | 2 months ago |
|
|
d13982857f | 2 months ago |
|
|
26b6390c5d | 2 months ago |
|
|
a1ecc77bbf | 2 months ago |
|
|
659b971314 | 2 months ago |
|
|
384eb58528 | 2 months ago |
|
|
0210408f49 | 2 months ago |
|
|
005589ac49 | 2 months ago |
|
|
f6563858e3 | 2 months ago |
|
|
33aa051fb3 | 2 months ago |
|
|
ed4abf927e | 2 months ago |
|
|
d79fbbb4ea | 2 months ago |
|
|
9b3e8e0e54 | 2 months ago |
|
|
885d563780 | 2 months ago |
|
|
dfc96a769c | 2 months ago |
|
|
b925012b78 | 2 months ago |
|
|
09c08ba9f3 | 2 months ago |
|
|
17e6b564ea | 2 months ago |
|
|
e8244c0fad | 2 months ago |
|
|
a69b944ab2 | 2 months ago |
|
|
5c48c5d87e | 2 months ago |
|
|
70024a0ce4 | 2 months ago |
|
|
5d8ed8b1e3 | 2 months ago |
|
|
a67ea7cfee | 2 months ago |
|
|
94548cb17d | 2 months ago |
|
|
9f74135f2d | 2 months ago |
|
|
f9857ad318 | 2 months ago |
|
|
aa1935e759 | 2 months ago |
@ -0,0 +1,3 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager">
|
||||
<output url="file://$PROJECT_DIR$/out" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,3 @@
|
||||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
@ -0,0 +1,292 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="WizardSettings">
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="imageWizard">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="imageAssetPanel">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="actionbar">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="clipArt">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
<entry key="imagePath" value="C:\Users\asus\AppData\Local\Temp\ic_android_black_24dp.xml" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="text">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="textAsset">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="launcher">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="foregroundClipArt">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="imagePath" value="C:\Users\asus\AppData\Local\Temp\ic_android_black_24dp.xml" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="foregroundImage">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
<entry key="imagePath" value="C:\Users\asus\Desktop\logo.png" />
|
||||
<entry key="scalingPercent" value="102" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="foregroundText">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="foregroundTextAsset">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="launcherLegacy">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="clipArt">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
<entry key="imagePath" value="C:\Users\asus\AppData\Local\Temp\ic_android_black_24dp.xml" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="text">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="textAsset">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="notification">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="clipArt">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
<entry key="imagePath" value="C:\Users\asus\AppData\Local\Temp\ic_android_black_24dp.xml" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="text">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="textAsset">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="tvBanner">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="foregroundText">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="tvChannel">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="children">
|
||||
<map>
|
||||
<entry key="foregroundClipArt">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="imagePath" value="C:\Users\asus\AppData\Local\Temp\ic_android_black_24dp.xml" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="foregroundImage">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="foregroundText">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
<entry key="foregroundTextAsset">
|
||||
<value>
|
||||
<PersistentState>
|
||||
<option name="values">
|
||||
<map>
|
||||
<entry key="color" value="000000" />
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</PersistentState>
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
||||
@ -1,6 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
<component name="VcsProjectSettings">
|
||||
<option name="detectVcsMappingsAutomatically" value="false" />
|
||||
</component>
|
||||
</project>
|
||||
@ -0,0 +1,3 @@
|
||||
{
|
||||
"java.configuration.updateBuildConfiguration": "interactive"
|
||||
}
|
||||
|
After Width: | Height: | Size: 168 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 692 KiB |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 889 KiB |
|
After Width: | Height: | Size: 284 KiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 107 KiB |
|
After Width: | Height: | Size: 388 KiB |
|
After Width: | Height: | Size: 611 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 435 KiB |
|
After Width: | Height: | Size: 179 KiB |
@ -0,0 +1,76 @@
|
||||
package adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.startsmake.llrisetabbardemo.R;
|
||||
import com.startsmake.llrisetabbardemo.model.Product;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ProductAdapter extends RecyclerView.Adapter<ProductAdapter.ViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private List<Product> itemList;
|
||||
private OnItemClickListener listener;
|
||||
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(Product item);
|
||||
}
|
||||
|
||||
public ProductAdapter(Context context, List<Product> itemList, OnItemClickListener listener) {
|
||||
this.context = context;
|
||||
this.itemList = itemList;
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.item_product, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
Product item = itemList.get(position);
|
||||
|
||||
holder.tvTitle.setText(item.getName());
|
||||
holder.tvDescription.setText(item.getDescription());
|
||||
holder.tvPrice.setText(String.format("¥%.2f", item.getPrice()));
|
||||
holder.tvCategory.setText(item.getCategory());
|
||||
|
||||
// 设置点击事件
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (listener != null) {
|
||||
listener.onItemClick(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return itemList.size();
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvTitle;
|
||||
TextView tvDescription;
|
||||
TextView tvCategory;
|
||||
TextView tvPrice;
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tvTitle = itemView.findViewById(R.id.product_name);
|
||||
tvDescription = itemView.findViewById(R.id.product_description);
|
||||
tvCategory = itemView.findViewById(R.id.product_category);
|
||||
tvPrice = itemView.findViewById(R.id.product_price);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,126 @@
|
||||
package com.example.llrisetabbardemo.models;
|
||||
|
||||
import com.example.llrisetabbardemo.utils.PriceEstimator;
|
||||
|
||||
public class ProductRecognitionInfo {
|
||||
private String productName; // 商品名称
|
||||
private String category; // 商品类别
|
||||
private String description; // 商品描述
|
||||
private PriceEstimator.EstimatedPrice estimatedPrice; // 预估价格信息
|
||||
private float recognitionConfidence; // 识别可信度
|
||||
private String imagePath; // 用于识别的图片路径
|
||||
|
||||
// 默认构造函数
|
||||
public ProductRecognitionInfo() {
|
||||
}
|
||||
|
||||
// 完整构造函数
|
||||
public ProductRecognitionInfo(String productName, String category,
|
||||
String description, float recognitionConfidence,
|
||||
String imagePath) {
|
||||
this.productName = productName;
|
||||
this.category = category;
|
||||
this.description = description;
|
||||
this.recognitionConfidence = recognitionConfidence;
|
||||
this.imagePath = imagePath;
|
||||
}
|
||||
|
||||
// Getters and Setters
|
||||
public String getProductName() {
|
||||
return productName;
|
||||
}
|
||||
|
||||
public void setProductName(String productName) {
|
||||
this.productName = productName;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public PriceEstimator.EstimatedPrice getEstimatedPrice() {
|
||||
return estimatedPrice;
|
||||
}
|
||||
|
||||
public void setEstimatedPrice(PriceEstimator.EstimatedPrice estimatedPrice) {
|
||||
this.estimatedPrice = estimatedPrice;
|
||||
}
|
||||
|
||||
public float getRecognitionConfidence() {
|
||||
return recognitionConfidence;
|
||||
}
|
||||
|
||||
public void setRecognitionConfidence(float recognitionConfidence) {
|
||||
this.recognitionConfidence = recognitionConfidence;
|
||||
}
|
||||
|
||||
public String getImagePath() {
|
||||
return imagePath;
|
||||
}
|
||||
|
||||
public void setImagePath(String imagePath) {
|
||||
this.imagePath = imagePath;
|
||||
}
|
||||
|
||||
// 获取格式化的价格字符串
|
||||
public String getFormattedPrice() {
|
||||
if (estimatedPrice != null) {
|
||||
return "¥" + String.format("%.2f", estimatedPrice.suggestedPrice);
|
||||
}
|
||||
return "--";
|
||||
}
|
||||
|
||||
// 获取价格建议范围字符串
|
||||
public String getFormattedPriceRange() {
|
||||
if (estimatedPrice != null) {
|
||||
return "¥" + String.format("%.2f", estimatedPrice.minSuggestedPrice) +
|
||||
" - ¥" + String.format("%.2f", estimatedPrice.maxSuggestedPrice);
|
||||
}
|
||||
return "--";
|
||||
}
|
||||
|
||||
// 获取格式化的可信度百分比
|
||||
public String getFormattedConfidence() {
|
||||
return String.format("%.1f%%", recognitionConfidence * 100);
|
||||
}
|
||||
|
||||
// 检查是否有效(有商品名称和类别)
|
||||
public boolean isValid() {
|
||||
return productName != null && !productName.isEmpty() &&
|
||||
category != null && !category.isEmpty();
|
||||
}
|
||||
|
||||
// 克隆对象
|
||||
public ProductRecognitionInfo clone() {
|
||||
ProductRecognitionInfo clone = new ProductRecognitionInfo();
|
||||
clone.productName = this.productName;
|
||||
clone.category = this.category;
|
||||
clone.description = this.description;
|
||||
clone.recognitionConfidence = this.recognitionConfidence;
|
||||
clone.imagePath = this.imagePath;
|
||||
clone.estimatedPrice = this.estimatedPrice;
|
||||
return clone;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ProductRecognitionInfo{" +
|
||||
"productName='" + productName + '\'' +
|
||||
", category='" + category + '\'' +
|
||||
", confidence=" + recognitionConfidence +
|
||||
", price=" + (estimatedPrice != null ? estimatedPrice.suggestedPrice : "null") +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,189 @@
|
||||
package com.example.llrisetabbardemo.utils;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Matrix;
|
||||
import android.media.ExifInterface;
|
||||
import android.os.Environment;
|
||||
import android.util.Base64;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class ImageUtils {
|
||||
|
||||
// 最大图片大小 (1MB)
|
||||
private static final int MAX_IMAGE_SIZE = 1024 * 1024;
|
||||
|
||||
// 图片压缩质量
|
||||
private static final int COMPRESS_QUALITY = 80;
|
||||
|
||||
/**
|
||||
* 压缩图片文件
|
||||
* @param imagePath 原始图片路径
|
||||
* @return 压缩后的图片路径
|
||||
*/
|
||||
public static String compressImage(String imagePath) {
|
||||
try {
|
||||
// 获取图片旋转角度
|
||||
int rotateAngle = getImageRotateAngle(imagePath);
|
||||
|
||||
// 解码图片,获取原始尺寸
|
||||
BitmapFactory.Options options = new BitmapFactory.Options();
|
||||
options.inJustDecodeBounds = true;
|
||||
BitmapFactory.decodeFile(imagePath, options);
|
||||
|
||||
// 计算采样率,使图片尺寸合理
|
||||
int inSampleSize = calculateInSampleSize(options, 1024, 1024);
|
||||
options.inSampleSize = inSampleSize;
|
||||
options.inJustDecodeBounds = false;
|
||||
|
||||
// 解码并旋转图片
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
|
||||
if (rotateAngle != 0) {
|
||||
bitmap = rotateBitmap(bitmap, rotateAngle);
|
||||
}
|
||||
|
||||
// 压缩图片到指定大小
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
int quality = COMPRESS_QUALITY;
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
|
||||
|
||||
// 如果压缩后的图片还是太大,继续压缩
|
||||
while (baos.toByteArray().length > MAX_IMAGE_SIZE && quality > 10) {
|
||||
baos.reset();
|
||||
quality -= 10;
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
|
||||
}
|
||||
|
||||
// 保存压缩后的图片
|
||||
String compressedPath = createCompressedImagePath();
|
||||
FileOutputStream fos = new FileOutputStream(compressedPath);
|
||||
fos.write(baos.toByteArray());
|
||||
fos.flush();
|
||||
fos.close();
|
||||
|
||||
bitmap.recycle();
|
||||
return compressedPath;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return imagePath; // 如果压缩失败,返回原始路径
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 将图片转换为Base64字符串
|
||||
* @param imagePath 图片路径
|
||||
* @return Base64编码的字符串
|
||||
*/
|
||||
public static String imageToBase64(String imagePath) {
|
||||
try {
|
||||
// 先压缩图片
|
||||
String compressedPath = compressImage(imagePath);
|
||||
|
||||
// 读取压缩后的图片
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(compressedPath);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, COMPRESS_QUALITY, baos);
|
||||
byte[] imageBytes = baos.toByteArray();
|
||||
|
||||
bitmap.recycle();
|
||||
|
||||
// 转换为Base64
|
||||
return Base64.encodeToString(imageBytes, Base64.DEFAULT);
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片旋转角度
|
||||
*/
|
||||
private static int getImageRotateAngle(String imagePath) {
|
||||
try {
|
||||
ExifInterface exif = new ExifInterface(imagePath);
|
||||
int orientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL);
|
||||
switch (orientation) {
|
||||
case ExifInterface.ORIENTATION_ROTATE_90:
|
||||
return 90;
|
||||
case ExifInterface.ORIENTATION_ROTATE_180:
|
||||
return 180;
|
||||
case ExifInterface.ORIENTATION_ROTATE_270:
|
||||
return 270;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 旋转图片
|
||||
*/
|
||||
private static Bitmap rotateBitmap(Bitmap bitmap, int degrees) {
|
||||
if (degrees != 0 && bitmap != null) {
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setRotate(degrees, (float) bitmap.getWidth() / 2, (float) bitmap.getHeight() / 2);
|
||||
try {
|
||||
Bitmap rotatedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
|
||||
bitmap.recycle();
|
||||
return rotatedBitmap;
|
||||
} catch (OutOfMemoryError e) {
|
||||
e.printStackTrace();
|
||||
return bitmap;
|
||||
}
|
||||
}
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算图片采样率
|
||||
*/
|
||||
private static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
|
||||
final int height = options.outHeight;
|
||||
final int width = options.outWidth;
|
||||
int inSampleSize = 1;
|
||||
|
||||
if (height > reqHeight || width > reqWidth) {
|
||||
final int halfHeight = height / 2;
|
||||
final int halfWidth = width / 2;
|
||||
|
||||
while ((halfHeight / inSampleSize) >= reqHeight && (halfWidth / inSampleSize) >= reqWidth) {
|
||||
inSampleSize *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return inSampleSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建压缩后的图片保存路径
|
||||
*/
|
||||
private static String createCompressedImagePath() throws IOException {
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
|
||||
String imageFileName = "COMPRESSED_" + timeStamp + ".jpg";
|
||||
|
||||
File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
||||
if (!storageDir.exists()) {
|
||||
storageDir.mkdirs();
|
||||
}
|
||||
|
||||
File imageFile = new File(storageDir, imageFileName);
|
||||
if (!imageFile.exists()) {
|
||||
imageFile.createNewFile();
|
||||
}
|
||||
|
||||
return imageFile.getAbsolutePath();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,111 @@
|
||||
package com.example.llrisetabbardemo.utils;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.os.Environment;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
public class VideoUtils {
|
||||
|
||||
/**
|
||||
* 从视频中提取关键帧
|
||||
* @param videoPath 视频路径
|
||||
* @return 提取的关键帧图片路径
|
||||
*/
|
||||
public static String extractKeyFrame(String videoPath) {
|
||||
try {
|
||||
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
|
||||
retriever.setDataSource(videoPath);
|
||||
|
||||
// 获取视频时长
|
||||
String durationStr = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
|
||||
long duration = durationStr != null ? Long.parseLong(durationStr) : 0;
|
||||
|
||||
// 提取视频中间部分的帧作为关键帧
|
||||
long frameTime = duration / 2;
|
||||
Bitmap keyFrame = retriever.getFrameAtTime(frameTime * 1000, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
|
||||
|
||||
retriever.release();
|
||||
|
||||
if (keyFrame != null) {
|
||||
// 保存关键帧为图片
|
||||
String framePath = saveBitmapToFile(keyFrame);
|
||||
keyFrame.recycle();
|
||||
return framePath;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存Bitmap到文件
|
||||
* @param bitmap 位图
|
||||
* @return 保存的文件路径
|
||||
*/
|
||||
private static String saveBitmapToFile(Bitmap bitmap) {
|
||||
try {
|
||||
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
|
||||
String imageFileName = "VIDEO_FRAME_" + timeStamp + ".jpg";
|
||||
|
||||
File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
|
||||
if (!storageDir.exists()) {
|
||||
storageDir.mkdirs();
|
||||
}
|
||||
|
||||
File imageFile = new File(storageDir, imageFileName);
|
||||
FileOutputStream fos = new FileOutputStream(imageFile);
|
||||
|
||||
// 压缩并保存图片
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, fos);
|
||||
fos.flush();
|
||||
fos.close();
|
||||
|
||||
return imageFile.getAbsolutePath();
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查文件是否为视频文件
|
||||
* @param filePath 文件路径
|
||||
* @return 是否为视频
|
||||
*/
|
||||
public static boolean isVideoFile(String filePath) {
|
||||
if (filePath == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String extension = getFileExtension(filePath).toLowerCase();
|
||||
return extension.equals("mp4") ||
|
||||
extension.equals("3gp") ||
|
||||
extension.equals("mov") ||
|
||||
extension.equals("avi") ||
|
||||
extension.equals("mkv");
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件扩展名
|
||||
* @param filePath 文件路径
|
||||
* @return 扩展名
|
||||
*/
|
||||
private static String getFileExtension(String filePath) {
|
||||
int lastDotIndex = filePath.lastIndexOf(".");
|
||||
if (lastDotIndex > 0 && lastDotIndex < filePath.length() - 1) {
|
||||
return filePath.substring(lastDotIndex + 1);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,158 @@
|
||||
package com.startsmake.llrisetabbardemo.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.startsmake.llrisetabbardemo.R;
|
||||
import com.startsmake.llrisetabbardemo.adapter.AddressAdapter;
|
||||
import com.startsmake.llrisetabbardemo.model.Address;
|
||||
import com.startsmake.llrisetabbardemo.service.AddressService;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class AddressListActivity extends AppCompatActivity {
|
||||
|
||||
private RecyclerView rvAddresses;
|
||||
private Button btnAddAddress;
|
||||
private AddressAdapter addressAdapter;
|
||||
private AddressService addressService;
|
||||
private boolean isFromOrder;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_address_list);
|
||||
|
||||
// 检查是否来自订单页面
|
||||
isFromOrder = getIntent().getBooleanExtra("from_order", false);
|
||||
|
||||
initViews();
|
||||
initServices();
|
||||
loadAddresses();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
rvAddresses = findViewById(R.id.rv_addresses);
|
||||
btnAddAddress = findViewById(R.id.btn_add_address);
|
||||
|
||||
// 设置RecyclerView
|
||||
rvAddresses.setLayoutManager(new LinearLayoutManager(this));
|
||||
addressAdapter = new AddressAdapter(this, null, new AddressAdapter.OnAddressClickListener() {
|
||||
@Override
|
||||
public void onSelect(Address address) {
|
||||
// 如果从订单页面跳转过来,选择地址后返回
|
||||
if (isFromOrder) {
|
||||
Intent resultIntent = new Intent();
|
||||
resultIntent.putExtra("selected_address", address);
|
||||
setResult(RESULT_OK, resultIntent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEdit(Address address) {
|
||||
// 编辑地址
|
||||
Intent intent = new Intent(AddressListActivity.this, EditAddressActivity.class);
|
||||
intent.putExtra("address", address);
|
||||
startActivityForResult(intent, 100);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDelete(Address address) {
|
||||
// 删除地址
|
||||
deleteAddress(address);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSetDefault(Address address) {
|
||||
// 设置默认地址
|
||||
setDefaultAddress(address);
|
||||
}
|
||||
});
|
||||
rvAddresses.setAdapter(addressAdapter);
|
||||
|
||||
// 添加新地址按钮
|
||||
btnAddAddress.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(AddressListActivity.this, EditAddressActivity.class);
|
||||
startActivityForResult(intent, 100);
|
||||
}
|
||||
});
|
||||
|
||||
// 返回按钮
|
||||
findViewById(R.id.tv_back).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onBackPressed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initServices() {
|
||||
addressService = AddressService.getInstance(this);
|
||||
}
|
||||
|
||||
private void loadAddresses() {
|
||||
addressService.getUserAddresses(new AddressService.AddressCallback() {
|
||||
@Override
|
||||
public void onSuccess(List<Address> addresses) {
|
||||
addressAdapter.setAddresses(addresses);
|
||||
addressAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String message) {
|
||||
Toast.makeText(AddressListActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void deleteAddress(final Address address) {
|
||||
addressService.deleteAddress(address.getId(), new AddressService.OperationCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
Toast.makeText(AddressListActivity.this, "地址删除成功", Toast.LENGTH_SHORT).show();
|
||||
// 重新加载地址列表
|
||||
loadAddresses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String message) {
|
||||
Toast.makeText(AddressListActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setDefaultAddress(final Address address) {
|
||||
addressService.setDefaultAddress(address.getId(), new AddressService.OperationCallback() {
|
||||
@Override
|
||||
public void onSuccess() {
|
||||
Toast.makeText(AddressListActivity.this, "设置默认地址成功", Toast.LENGTH_SHORT).show();
|
||||
// 重新加载地址列表
|
||||
loadAddresses();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String message) {
|
||||
Toast.makeText(AddressListActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == 100 && resultCode == RESULT_OK) {
|
||||
// 地址添加或编辑成功,重新加载列表
|
||||
loadAddresses();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,182 @@
|
||||
package com.startsmake.llrisetabbardemo.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.startsmake.llrisetabbardemo.R;
|
||||
import com.startsmake.llrisetabbardemo.model.Address;
|
||||
import com.startsmake.llrisetabbardemo.service.AddressService;
|
||||
|
||||
public class EditAddressActivity extends AppCompatActivity {
|
||||
|
||||
private EditText etName;
|
||||
private EditText etPhone;
|
||||
private EditText etProvince;
|
||||
private EditText etCity;
|
||||
private EditText etDistrict;
|
||||
private EditText etDetailAddress;
|
||||
private CheckBox cbDefault;
|
||||
private Button btnSave;
|
||||
|
||||
private Address address;
|
||||
private AddressService addressService;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_edit_address);
|
||||
|
||||
initViews();
|
||||
initServices();
|
||||
initData();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
etName = findViewById(R.id.et_name);
|
||||
etPhone = findViewById(R.id.et_phone);
|
||||
etProvince = findViewById(R.id.et_province);
|
||||
etCity = findViewById(R.id.et_city);
|
||||
etDistrict = findViewById(R.id.et_district);
|
||||
etDetailAddress = findViewById(R.id.et_detail_address);
|
||||
cbDefault = findViewById(R.id.cb_default);
|
||||
btnSave = findViewById(R.id.btn_save);
|
||||
|
||||
btnSave.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
saveAddress();
|
||||
}
|
||||
});
|
||||
|
||||
// 返回按钮
|
||||
findViewById(R.id.tv_back).setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
onBackPressed();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initServices() {
|
||||
addressService = AddressService.getInstance(this);
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
// 检查是否是编辑模式
|
||||
address = getIntent().getParcelableExtra("address");
|
||||
if (address != null) {
|
||||
// 编辑模式
|
||||
etName.setText(address.getName());
|
||||
etPhone.setText(address.getPhone());
|
||||
etProvince.setText(address.getProvince());
|
||||
etCity.setText(address.getCity());
|
||||
etDistrict.setText(address.getDistrict());
|
||||
etDetailAddress.setText(address.getDetailAddress());
|
||||
cbDefault.setChecked(address.isDefault());
|
||||
} else {
|
||||
// 新增模式,默认设为默认地址
|
||||
cbDefault.setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void saveAddress() {
|
||||
// 验证输入
|
||||
String name = etName.getText().toString().trim();
|
||||
String phone = etPhone.getText().toString().trim();
|
||||
String province = etProvince.getText().toString().trim();
|
||||
String city = etCity.getText().toString().trim();
|
||||
String district = etDistrict.getText().toString().trim();
|
||||
String detailAddress = etDetailAddress.getText().toString().trim();
|
||||
boolean isDefault = cbDefault.isChecked();
|
||||
|
||||
if (TextUtils.isEmpty(name)) {
|
||||
Toast.makeText(this, "请输入收件人姓名", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(phone)) {
|
||||
Toast.makeText(this, "请输入联系电话", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// 简单的手机号验证
|
||||
if (!phone.matches("^1[3-9]\\d{9}$")) {
|
||||
Toast.makeText(this, "请输入正确的手机号码", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(province)) {
|
||||
Toast.makeText(this, "请输入省份", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(city)) {
|
||||
Toast.makeText(this, "请输入城市", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(district)) {
|
||||
Toast.makeText(this, "请输入区县", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(detailAddress)) {
|
||||
Toast.makeText(this, "请输入详细地址", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建或更新地址对象
|
||||
if (address == null) {
|
||||
// 新增地址
|
||||
address = new Address();
|
||||
}
|
||||
|
||||
address.setName(name);
|
||||
address.setPhone(phone);
|
||||
address.setProvince(province);
|
||||
address.setCity(city);
|
||||
address.setDistrict(district);
|
||||
address.setDetailAddress(detailAddress);
|
||||
address.setDefault(isDefault);
|
||||
|
||||
// 保存地址
|
||||
if (TextUtils.isEmpty(address.getId())) {
|
||||
// 新增
|
||||
addressService.addAddress(address, new AddressService.SingleAddressCallback() {
|
||||
@Override
|
||||
public void onSuccess(Address newAddress) {
|
||||
Toast.makeText(EditAddressActivity.this, "地址添加成功", Toast.LENGTH_SHORT).show();
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String message) {
|
||||
Toast.makeText(EditAddressActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 更新
|
||||
addressService.updateAddress(address, new AddressService.SingleAddressCallback() {
|
||||
@Override
|
||||
public void onSuccess(Address updatedAddress) {
|
||||
Toast.makeText(EditAddressActivity.this, "地址更新成功", Toast.LENGTH_SHORT).show();
|
||||
setResult(RESULT_OK);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String message) {
|
||||
Toast.makeText(EditAddressActivity.this, message, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,82 @@
|
||||
package com.startsmake.llrisetabbardemo.activity;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.startsmake.llrisetabbardemo.R;
|
||||
import com.startsmake.llrisetabbardemo.adapter.ImagePreviewAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ImagePreviewActivity extends AppCompatActivity {
|
||||
|
||||
public static final String EXTRA_IMAGE_URLS = "image_urls";
|
||||
public static final String EXTRA_CURRENT_POSITION = "current_position";
|
||||
|
||||
private ViewPager2 viewPager;
|
||||
private TextView tvPosition;
|
||||
private ImageView ivClose;
|
||||
private List<String> imageUrls;
|
||||
private int currentPosition;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
// 全屏显示,隐藏状态栏
|
||||
getWindow().getDecorView().setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
);
|
||||
setContentView(R.layout.activity_image_preview);
|
||||
|
||||
// 获取传递的图片URL列表和当前位置
|
||||
if (getIntent() != null) {
|
||||
imageUrls = getIntent().getStringArrayListExtra(EXTRA_IMAGE_URLS);
|
||||
currentPosition = getIntent().getIntExtra(EXTRA_CURRENT_POSITION, 0);
|
||||
}
|
||||
|
||||
// 初始化视图
|
||||
viewPager = findViewById(R.id.view_pager_preview);
|
||||
tvPosition = findViewById(R.id.tv_position);
|
||||
ivClose = findViewById(R.id.iv_close);
|
||||
|
||||
// 设置适配器
|
||||
ImagePreviewAdapter adapter = new ImagePreviewAdapter(imageUrls);
|
||||
viewPager.setAdapter(adapter);
|
||||
viewPager.setCurrentItem(currentPosition, false);
|
||||
|
||||
// 更新位置显示
|
||||
updatePositionDisplay(currentPosition);
|
||||
|
||||
// 设置ViewPager页面变化监听
|
||||
viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
super.onPageSelected(position);
|
||||
currentPosition = position;
|
||||
updatePositionDisplay(position);
|
||||
}
|
||||
});
|
||||
|
||||
// 设置关闭按钮点击事件
|
||||
ivClose.setOnClickListener(v -> finish());
|
||||
}
|
||||
|
||||
private void updatePositionDisplay(int position) {
|
||||
if (imageUrls != null && !imageUrls.isEmpty()) {
|
||||
tvPosition.setText((position + 1) + "/" + imageUrls.size());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,271 @@
|
||||
package com.startsmake.llrisetabbardemo.activity;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.os.CountDownTimer;
|
||||
import android.widget.Button;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.google.android.material.textfield.TextInputEditText;
|
||||
import com.startsmake.llrisetabbardemo.R;
|
||||
import com.startsmake.llrisetabbardemo.manager.UserManager;
|
||||
import com.startsmake.llrisetabbardemo.model.User;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class RegisterActivity extends AppCompatActivity {
|
||||
|
||||
private TextInputEditText etPhone, etVerificationCode, etPassword;
|
||||
private Button btnSendCode, btnRegister;
|
||||
private ImageButton btnBack;
|
||||
private TextView tvLogin;
|
||||
|
||||
private CountDownTimer countDownTimer;
|
||||
private boolean isCounting = false;
|
||||
private String verificationCode = "";
|
||||
private UserManager userManager;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_register);
|
||||
|
||||
// 确保使用带Context参数的getInstance方法
|
||||
userManager = UserManager.getInstance(this);
|
||||
initViews();
|
||||
setupClickListeners();
|
||||
}
|
||||
|
||||
private void initViews() {
|
||||
etPhone = findViewById(R.id.et_phone);
|
||||
etVerificationCode = findViewById(R.id.et_verification_code);
|
||||
etPassword = findViewById(R.id.et_password);
|
||||
btnSendCode = findViewById(R.id.btn_send_code);
|
||||
btnRegister = findViewById(R.id.btn_register);
|
||||
btnBack = findViewById(R.id.btn_back);
|
||||
tvLogin = findViewById(R.id.tv_login);
|
||||
}
|
||||
|
||||
private void setupClickListeners() {
|
||||
// 返回按钮
|
||||
btnBack.setOnClickListener(v -> finish());
|
||||
|
||||
// 发送验证码
|
||||
btnSendCode.setOnClickListener(v -> sendVerificationCode());
|
||||
|
||||
// 注册按钮
|
||||
btnRegister.setOnClickListener(v -> attemptRegister());
|
||||
|
||||
// 立即登录
|
||||
tvLogin.setOnClickListener(v -> {
|
||||
Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
private void sendVerificationCode() {
|
||||
String phone = etPhone.getText().toString().trim();
|
||||
|
||||
if (!validatePhone(phone)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查用户是否已存在
|
||||
if (userManager.isPhoneRegistered(phone)) {
|
||||
etPhone.setError("该手机号已注册");
|
||||
etPhone.requestFocus();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCounting) {
|
||||
// 生成随机验证码
|
||||
verificationCode = generateVerificationCode();
|
||||
startCountDown();
|
||||
|
||||
// 模拟发送验证码(在实际应用中应该通过短信发送)
|
||||
Toast.makeText(this, "验证码已发送: " + verificationCode, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean validatePhone(String phone) {
|
||||
if (phone.isEmpty()) {
|
||||
etPhone.setError("请输入手机号");
|
||||
etPhone.requestFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (phone.length() != 11) {
|
||||
etPhone.setError("手机号格式不正确");
|
||||
etPhone.requestFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!phone.startsWith("1")) {
|
||||
etPhone.setError("手机号格式不正确");
|
||||
etPhone.requestFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private String generateVerificationCode() {
|
||||
Random random = new Random();
|
||||
int code = 100000 + random.nextInt(900000);
|
||||
return String.valueOf(code);
|
||||
}
|
||||
|
||||
private void startCountDown() {
|
||||
isCounting = true;
|
||||
btnSendCode.setEnabled(false);
|
||||
|
||||
countDownTimer = new CountDownTimer(60000, 1000) {
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
btnSendCode.setText(millisUntilFinished / 1000 + "秒后重发");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
isCounting = false;
|
||||
btnSendCode.setEnabled(true);
|
||||
btnSendCode.setText("发送验证码");
|
||||
// 验证码过期
|
||||
verificationCode = "";
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
|
||||
private void attemptRegister() {
|
||||
String phone = etPhone.getText().toString().trim();
|
||||
String code = etVerificationCode.getText().toString().trim();
|
||||
String password = etPassword.getText().toString().trim();
|
||||
|
||||
if (!validateRegisterInput(phone, code, password)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证验证码
|
||||
if (!code.equals(verificationCode)) {
|
||||
etVerificationCode.setError("验证码错误");
|
||||
etVerificationCode.requestFocus();
|
||||
Toast.makeText(this, "请输入正确的验证码", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查验证码是否过期
|
||||
if (verificationCode.isEmpty()) {
|
||||
etVerificationCode.setError("验证码已过期,请重新获取");
|
||||
etVerificationCode.requestFocus();
|
||||
Toast.makeText(this, "验证码已过期,请重新获取", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
||||
// 注册用户 - 使用后端API
|
||||
String username = "用户_" + phone.substring(phone.length() - 4); // 生成默认用户名
|
||||
userManager.registerUserWithApi(phone, password, username, new UserManager.AuthCallback() {
|
||||
@Override
|
||||
public void onSuccess(User user) {
|
||||
Toast.makeText(RegisterActivity.this, "注册成功!", Toast.LENGTH_SHORT).show();
|
||||
// 跳转到主页面
|
||||
Intent intent = new Intent(RegisterActivity.this, MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String message) {
|
||||
Toast.makeText(RegisterActivity.this, "注册失败: " + message, Toast.LENGTH_SHORT).show();
|
||||
etPhone.setError("注册失败,请重试");
|
||||
etPhone.requestFocus();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean validateRegisterInput(String phone, String code, String password) {
|
||||
// 验证手机号
|
||||
if (phone.isEmpty()) {
|
||||
etPhone.setError("请输入手机号");
|
||||
etPhone.requestFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (phone.length() != 11) {
|
||||
etPhone.setError("手机号格式不正确");
|
||||
etPhone.requestFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!phone.startsWith("1")) {
|
||||
etPhone.setError("手机号格式不正确");
|
||||
etPhone.requestFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证验证码
|
||||
if (code.isEmpty()) {
|
||||
etVerificationCode.setError("请输入验证码");
|
||||
etVerificationCode.requestFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (code.length() != 6) {
|
||||
etVerificationCode.setError("验证码格式不正确");
|
||||
etVerificationCode.requestFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if (password.isEmpty()) {
|
||||
etPassword.setError("请输入密码");
|
||||
etPassword.requestFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (password.length() < 6) {
|
||||
etPassword.setError("密码至少6位");
|
||||
etPassword.requestFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (password.length() > 20) {
|
||||
etPassword.setError("密码最多20位");
|
||||
etPassword.requestFocus();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void performRegisterSuccess(String phone, String password) {
|
||||
Toast.makeText(this, "注册成功!", Toast.LENGTH_SHORT).show();
|
||||
|
||||
// 自动登录
|
||||
User user = userManager.loginUser(phone, password);
|
||||
if (user != null) {
|
||||
// 跳转到主页面
|
||||
Intent intent = new Intent(RegisterActivity.this, MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
} else {
|
||||
// 如果自动登录失败,跳转到登录页面
|
||||
Toast.makeText(this, "注册成功,请登录", Toast.LENGTH_SHORT).show();
|
||||
Intent intent = new Intent(RegisterActivity.this, LoginActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
if (countDownTimer != null) {
|
||||
countDownTimer.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,127 @@
|
||||
package com.startsmake.llrisetabbardemo.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.startsmake.llrisetabbardemo.R;
|
||||
import com.startsmake.llrisetabbardemo.model.Address;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AddressAdapter extends RecyclerView.Adapter<AddressAdapter.AddressViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private List<Address> addresses;
|
||||
private OnAddressClickListener listener;
|
||||
|
||||
public interface OnAddressClickListener {
|
||||
void onSelect(Address address);
|
||||
void onEdit(Address address);
|
||||
void onDelete(Address address);
|
||||
void onSetDefault(Address address);
|
||||
}
|
||||
|
||||
public AddressAdapter(Context context, List<Address> addresses, OnAddressClickListener listener) {
|
||||
this.context = context;
|
||||
this.addresses = addresses != null ? addresses : new ArrayList<>();
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
public void setAddresses(List<Address> addresses) {
|
||||
this.addresses = addresses != null ? addresses : new ArrayList<>();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public AddressViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.item_address, parent, false);
|
||||
return new AddressViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull AddressViewHolder holder, int position) {
|
||||
final Address address = addresses.get(position);
|
||||
|
||||
// 显示地址信息
|
||||
holder.tvName.setText(address.getName());
|
||||
holder.tvPhone.setText(address.getPhone());
|
||||
holder.tvAddress.setText(address.getAddressWithContact());
|
||||
|
||||
// 显示默认标签
|
||||
if (address.isDefault()) {
|
||||
holder.tvDefault.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.tvDefault.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// 设置点击事件
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (listener != null) {
|
||||
listener.onSelect(address);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
holder.btnEdit.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (listener != null) {
|
||||
listener.onEdit(address);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
holder.btnDelete.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (listener != null) {
|
||||
listener.onDelete(address);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
holder.btnSetDefault.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (listener != null) {
|
||||
listener.onSetDefault(address);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return addresses.size();
|
||||
}
|
||||
|
||||
static class AddressViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvName;
|
||||
TextView tvPhone;
|
||||
TextView tvAddress;
|
||||
TextView tvDefault;
|
||||
TextView btnEdit;
|
||||
TextView btnDelete;
|
||||
TextView btnSetDefault;
|
||||
|
||||
AddressViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tvName = itemView.findViewById(R.id.tv_name);
|
||||
tvPhone = itemView.findViewById(R.id.tv_phone);
|
||||
tvAddress = itemView.findViewById(R.id.tv_address);
|
||||
tvDefault = itemView.findViewById(R.id.tv_default);
|
||||
btnEdit = itemView.findViewById(R.id.btn_edit);
|
||||
btnDelete = itemView.findViewById(R.id.btn_delete);
|
||||
btnSetDefault = itemView.findViewById(R.id.btn_set_default);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
package com.startsmake.llrisetabbardemo.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ImageView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.startsmake.llrisetabbardemo.R;
|
||||
import com.startsmake.llrisetabbardemo.activity.ChatActivity;
|
||||
import com.startsmake.llrisetabbardemo.model.Conversation;
|
||||
import java.util.List;
|
||||
|
||||
public class ConversationAdapter extends RecyclerView.Adapter<ConversationAdapter.ViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private List<Conversation> conversationList;
|
||||
|
||||
public ConversationAdapter(Context context, List<Conversation> conversationList) {
|
||||
this.context = context;
|
||||
this.conversationList = conversationList;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.item_conversation, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
Conversation conversation = conversationList.get(position);
|
||||
holder.tvUsername.setText(conversation.getUsername());
|
||||
holder.tvLastMessage.setText(conversation.getLastMessage());
|
||||
holder.tvTime.setText(conversation.getTime());
|
||||
|
||||
// 设置未读消息数量
|
||||
if (conversation.getUnreadCount() > 0) {
|
||||
holder.tvUnreadCount.setVisibility(View.VISIBLE);
|
||||
holder.tvUnreadCount.setText(String.valueOf(conversation.getUnreadCount()));
|
||||
} else {
|
||||
holder.tvUnreadCount.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// 显示测试账号标识
|
||||
if (conversation.getId().equals("13800138001") ||
|
||||
conversation.getId().equals("13800138002") ||
|
||||
conversation.getId().equals("13800138003")) {
|
||||
holder.ivTestAccount.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.ivTestAccount.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// 设置点击事件
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// 跳转到聊天页面
|
||||
Intent intent = new Intent(context, ChatActivity.class);
|
||||
intent.putExtra("chat_title", conversation.getUsername());
|
||||
intent.putExtra("user_id", conversation.getId());
|
||||
context.startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return conversationList.size();
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
TextView tvUsername;
|
||||
TextView tvLastMessage;
|
||||
TextView tvTime;
|
||||
TextView tvUnreadCount;
|
||||
ImageView ivTestAccount;
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
tvUsername = itemView.findViewById(R.id.tvUsername);
|
||||
tvLastMessage = itemView.findViewById(R.id.tvLastMessage);
|
||||
tvTime = itemView.findViewById(R.id.tvTime);
|
||||
tvUnreadCount = itemView.findViewById(R.id.tvUnreadCount);
|
||||
ivTestAccount = itemView.findViewById(R.id.ivTestAccount);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
package com.startsmake.llrisetabbardemo.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.startsmake.llrisetabbardemo.R;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ImageAdapter extends BaseAdapter {
|
||||
|
||||
private Context context;
|
||||
private List<Uri> imageUris;
|
||||
private static final int MAX_IMAGES = 9;
|
||||
|
||||
public ImageAdapter(Context context, List<Uri> imageUris) {
|
||||
this.context = context;
|
||||
this.imageUris = imageUris;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return Math.min(imageUris.size() + 1, MAX_IMAGES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
if (position < imageUris.size()) {
|
||||
return imageUris.get(position);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
ViewHolder holder;
|
||||
if (convertView == null) {
|
||||
convertView = LayoutInflater.from(context).inflate(R.layout.item_image, parent, false);
|
||||
holder = new ViewHolder();
|
||||
holder.imageView = convertView.findViewById(R.id.imageView);
|
||||
holder.deleteButton = convertView.findViewById(R.id.btnDelete);
|
||||
convertView.setTag(holder);
|
||||
} else {
|
||||
holder = (ViewHolder) convertView.getTag();
|
||||
}
|
||||
|
||||
if (position < imageUris.size()) {
|
||||
// 显示已选择的图片
|
||||
Uri imageUri = imageUris.get(position);
|
||||
Glide.with(context)
|
||||
.load(imageUri)
|
||||
.placeholder(android.R.drawable.ic_menu_gallery) // 使用系统图标作为占位符
|
||||
.into(holder.imageView);
|
||||
|
||||
holder.deleteButton.setVisibility(View.VISIBLE);
|
||||
holder.deleteButton.setOnClickListener(v -> {
|
||||
imageUris.remove(position);
|
||||
notifyDataSetChanged();
|
||||
});
|
||||
} else {
|
||||
// 显示添加按钮
|
||||
holder.imageView.setImageResource(android.R.drawable.ic_input_add); // 使用系统图标
|
||||
holder.deleteButton.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
return convertView;
|
||||
}
|
||||
|
||||
static class ViewHolder {
|
||||
ImageView imageView;
|
||||
ImageView deleteButton;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
package com.startsmake.llrisetabbardemo.adapter;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.startsmake.llrisetabbardemo.R;
|
||||
import com.startsmake.llrisetabbardemo.model.MessageItem;
|
||||
import java.util.List;
|
||||
|
||||
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.ViewHolder> {
|
||||
|
||||
private Context context;
|
||||
private List<MessageItem> messageList;
|
||||
private OnItemClickListener onItemClickListener;
|
||||
|
||||
public MessageAdapter(Context context, List<MessageItem> messageList) {
|
||||
this.context = context;
|
||||
this.messageList = messageList;
|
||||
}
|
||||
|
||||
// 添加点击监听接口
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(MessageItem item);
|
||||
}
|
||||
|
||||
public void setOnItemClickListener(OnItemClickListener listener) {
|
||||
this.onItemClickListener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.item_message, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
MessageItem item = messageList.get(position);
|
||||
|
||||
// 设置默认白色头像背景
|
||||
holder.ivAvatar.setBackgroundResource(R.drawable.bg_avatar_placeholder);
|
||||
|
||||
holder.tvTitle.setText(item.getTitle());
|
||||
holder.tvContent.setText(item.getContent());
|
||||
holder.tvTime.setText(item.getTime());
|
||||
|
||||
// 未读消息数量
|
||||
if (item.getUnreadCount() > 0) {
|
||||
holder.tvUnreadCount.setVisibility(View.VISIBLE);
|
||||
holder.tvUnreadCount.setText(String.valueOf(item.getUnreadCount()));
|
||||
} else {
|
||||
holder.tvUnreadCount.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// 官方标识
|
||||
if (item.isOfficial()) {
|
||||
holder.ivOfficial.setVisibility(View.VISIBLE);
|
||||
} else {
|
||||
holder.ivOfficial.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
// 添加点击事件
|
||||
holder.itemView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
if (onItemClickListener != null) {
|
||||
onItemClickListener.onItemClick(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return messageList.size();
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ImageView ivAvatar;
|
||||
TextView tvTitle;
|
||||
TextView tvContent;
|
||||
TextView tvTime;
|
||||
TextView tvUnreadCount;
|
||||
ImageView ivOfficial;
|
||||
|
||||
public ViewHolder(@NonNull View itemView) {
|
||||
super(itemView);
|
||||
ivAvatar = itemView.findViewById(R.id.ivAvatar);
|
||||
tvTitle = itemView.findViewById(R.id.tvTitle);
|
||||
tvContent = itemView.findViewById(R.id.tvContent);
|
||||
tvTime = itemView.findViewById(R.id.tvTime);
|
||||
tvUnreadCount = itemView.findViewById(R.id.tvUnreadCount);
|
||||
ivOfficial = itemView.findViewById(R.id.ivOfficial);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,89 @@
|
||||
package com.startsmake.llrisetabbardemo.adapter;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.startsmake.llrisetabbardemo.R;
|
||||
import com.startsmake.llrisetabbardemo.model.Shop;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ShopAdapter extends RecyclerView.Adapter<ShopAdapter.ViewHolder> {
|
||||
|
||||
private List<Shop> shopList;
|
||||
private OnItemClickListener onItemClickListener;
|
||||
|
||||
// 定义点击监听接口
|
||||
public interface OnItemClickListener {
|
||||
void onItemClick(Shop shop, int position);
|
||||
}
|
||||
|
||||
// 构造方法
|
||||
public ShopAdapter(List<Shop> shopList) {
|
||||
this.shopList = shopList;
|
||||
}
|
||||
|
||||
// 设置点击监听器(只有一个方法)
|
||||
public void setOnItemClickListener(OnItemClickListener listener) {
|
||||
this.onItemClickListener = listener;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||
View view = LayoutInflater.from(parent.getContext())
|
||||
.inflate(R.layout.item_shop, parent, false);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||
Shop shop = shopList.get(position);
|
||||
holder.textName.setText(shop.getName());
|
||||
holder.textDescription.setText(shop.getDescription());
|
||||
|
||||
if (shop.getImage() != null) {
|
||||
holder.imageView.setImageBitmap(shop.getImage());
|
||||
} else {
|
||||
// 使用自定义的默认图片
|
||||
holder.imageView.setImageResource(R.drawable.ic_about);
|
||||
}
|
||||
|
||||
// 添加点击事件
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
if (onItemClickListener != null) {
|
||||
onItemClickListener.onItemClick(shop, position);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return shopList != null ? shopList.size() : 0;
|
||||
}
|
||||
|
||||
// 更新数据方法
|
||||
public void updateData(List<Shop> newShopList) {
|
||||
this.shopList = newShopList;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
public ImageView imageView;
|
||||
public TextView textName;
|
||||
public TextView textDescription;
|
||||
|
||||
public ViewHolder(View view) {
|
||||
super(view);
|
||||
imageView = view.findViewById(R.id.imgShop);
|
||||
textName = view.findViewById(R.id.txtShopName);
|
||||
textDescription = view.findViewById(R.id.txtShopDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.startsmake.llrisetabbardemo.api;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class ApiClient {
|
||||
public static final String BASE_URL = "http://10.0.2.2:8080/api/";
|
||||
private static Retrofit retrofit = null;
|
||||
|
||||
public static Retrofit getClient() {
|
||||
if (retrofit == null) {
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.connectTimeout(30, TimeUnit.SECONDS)
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
.writeTimeout(30, TimeUnit.SECONDS)
|
||||
.build();
|
||||
|
||||
retrofit = new Retrofit.Builder()
|
||||
.baseUrl(BASE_URL)
|
||||
.client(client)
|
||||
.addConverterFactory(GsonConverterFactory.create())
|
||||
.build();
|
||||
}
|
||||
return retrofit;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,214 @@
|
||||
package com.startsmake.llrisetabbardemo.api;
|
||||
|
||||
import com.startsmake.llrisetabbardemo.api.response.BaseResponse;
|
||||
import com.startsmake.llrisetabbardemo.api.response.ChatMessageResponse;
|
||||
import com.startsmake.llrisetabbardemo.api.response.ProductResponse;
|
||||
import com.startsmake.llrisetabbardemo.api.response.UserResponse;
|
||||
import com.startsmake.llrisetabbardemo.model.Address;
|
||||
import com.startsmake.llrisetabbardemo.model.Conversation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import retrofit2.http.Body;
|
||||
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Field;
|
||||
import retrofit2.http.FormUrlEncoded;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.Header;
|
||||
import retrofit2.http.DELETE;
|
||||
import retrofit2.http.GET;
|
||||
import retrofit2.http.POST;
|
||||
import retrofit2.http.PUT;
|
||||
import retrofit2.http.Path;
|
||||
import retrofit2.http.Query;
|
||||
|
||||
public interface ApiService {
|
||||
// 获取API状态
|
||||
@GET("api")
|
||||
Call<BaseResponse> getApiStatus();
|
||||
|
||||
// 用户登录
|
||||
@FormUrlEncoded
|
||||
@POST("api/login")
|
||||
Call<UserResponse> login(@Field("phone") String phone, @Field("password") String password);
|
||||
|
||||
// 用户注册
|
||||
@FormUrlEncoded
|
||||
@POST("api/register")
|
||||
Call<UserResponse> register(@Field("phone") String phone, @Field("password") String password,
|
||||
@Field("username") String username);
|
||||
|
||||
// 获取商品列表
|
||||
@GET("api/products")
|
||||
Call<BaseResponse<List<ProductResponse>>> getProducts();
|
||||
|
||||
// 搜索商品
|
||||
@GET("api/products/search")
|
||||
Call<BaseResponse<List<ProductResponse>>> searchProducts(@Query("keyword") String keyword);
|
||||
|
||||
// 获取商品详情
|
||||
@GET("api/products/detail")
|
||||
Call<BaseResponse<ProductResponse>> getProductDetail(@Query("id") String productId);
|
||||
|
||||
// 获取用户信息
|
||||
@GET("api/user/info")
|
||||
Call<UserResponse> getUserInfo(@Header("Authorization") String token);
|
||||
|
||||
// 更新用户信息
|
||||
@POST("/api/users/update")
|
||||
Call<BaseResponse<UserResponse.UserInfo>> updateUserInfo(
|
||||
@Header("Authorization") String token,
|
||||
@Body Map<String, Object> userInfo
|
||||
);
|
||||
|
||||
// 获取会话列表
|
||||
@GET("/api/conversations")
|
||||
Call<BaseResponse<List<Conversation>>> getUserConversations(
|
||||
@Header("Authorization") String token
|
||||
);
|
||||
|
||||
// 获取会话消息
|
||||
@GET("/api/conversations/messages")
|
||||
Call<BaseResponse<List<ChatMessageResponse>>> getConversationMessages(
|
||||
@Header("Authorization") String token,
|
||||
@Query("userId") String userId,
|
||||
@Query("targetId") String targetId
|
||||
);
|
||||
|
||||
// 发送消息
|
||||
@POST("/api/messages")
|
||||
Call<BaseResponse<ChatMessageResponse>> sendMessage(
|
||||
@Header("Authorization") String token,
|
||||
@Query("targetId") String targetId,
|
||||
@Query("content") String content,
|
||||
@Query("itemId") String itemId
|
||||
);
|
||||
|
||||
// 标记消息已读
|
||||
@PUT("/api/messages/read")
|
||||
Call<BaseResponse> markMessagesAsRead(
|
||||
@Header("Authorization") String token,
|
||||
@Query("userId") String userId,
|
||||
@Query("targetId") String targetId
|
||||
);
|
||||
|
||||
// 订单相关接口
|
||||
|
||||
// 创建订单
|
||||
@POST("/api/orders")
|
||||
Call<BaseResponse<Map<String, Object>>> createOrder(
|
||||
@Header("Authorization") String token,
|
||||
@Body Map<String, Object> orderData
|
||||
);
|
||||
|
||||
// 获取订单详情
|
||||
@GET("/api/orders/{id}")
|
||||
Call<BaseResponse<Map<String, Object>>> getOrderDetail(
|
||||
@Header("Authorization") String token,
|
||||
@Path("id") String orderId
|
||||
);
|
||||
|
||||
// 通过订单号获取订单
|
||||
@GET("/api/orders/order-no/{orderNo}")
|
||||
Call<BaseResponse<Map<String, Object>>> getOrderByOrderNo(
|
||||
@Header("Authorization") String token,
|
||||
@Path("orderNo") String orderNo
|
||||
);
|
||||
|
||||
// 获取买家订单列表
|
||||
@GET("/api/orders/buyer/{buyerId}")
|
||||
Call<BaseResponse<List<Map<String, Object>>>> getBuyerOrders(
|
||||
@Header("Authorization") String token,
|
||||
@Path("buyerId") String buyerId
|
||||
);
|
||||
|
||||
// 获取卖家订单列表
|
||||
@GET("/api/orders/seller/{sellerId}")
|
||||
Call<BaseResponse<List<Map<String, Object>>>> getSellerOrders(
|
||||
@Header("Authorization") String token,
|
||||
@Path("sellerId") String sellerId
|
||||
);
|
||||
|
||||
// 更新订单状态
|
||||
@PUT("/api/orders/{id}/status")
|
||||
Call<BaseResponse<Map<String, Object>>> updateOrderStatus(
|
||||
@Header("Authorization") String token,
|
||||
@Path("id") String orderId,
|
||||
@Body Map<String, Object> statusData
|
||||
);
|
||||
|
||||
// 更新订单支付信息
|
||||
@PUT("/api/orders/{id}/payment")
|
||||
Call<BaseResponse<Map<String, Object>>> updateOrderPayment(
|
||||
@Header("Authorization") String token,
|
||||
@Path("id") String orderId,
|
||||
@Body Map<String, Object> paymentData
|
||||
);
|
||||
|
||||
// 取消订单
|
||||
@PUT("/api/orders/{id}/cancel")
|
||||
Call<BaseResponse> cancelOrder(
|
||||
@Header("Authorization") String token,
|
||||
@Path("id") String orderId
|
||||
);
|
||||
|
||||
// 获取买家指定状态的订单
|
||||
@GET("/api/orders/buyer/{buyerId}/status/{status}")
|
||||
Call<BaseResponse<List<Map<String, Object>>>> getBuyerOrdersByStatus(
|
||||
@Header("Authorization") String token,
|
||||
@Path("buyerId") String buyerId,
|
||||
@Path("status") String status
|
||||
);
|
||||
|
||||
// 获取卖家指定状态的订单
|
||||
@GET("/api/orders/seller/{sellerId}/status/{status}")
|
||||
Call<BaseResponse<List<Map<String, Object>>>> getSellerOrdersByStatus(
|
||||
@Header("Authorization") String token,
|
||||
@Path("sellerId") String sellerId,
|
||||
@Path("status") String status
|
||||
);
|
||||
|
||||
// 地址管理相关接口
|
||||
|
||||
// 获取用户地址列表
|
||||
@GET("/api/addresses")
|
||||
Call<BaseResponse<List<Address>>> getUserAddresses(
|
||||
@Header("Authorization") String token
|
||||
);
|
||||
|
||||
// 获取用户默认地址
|
||||
@GET("/api/addresses/default")
|
||||
Call<BaseResponse<Address>> getDefaultAddress(
|
||||
@Header("Authorization") String token
|
||||
);
|
||||
|
||||
// 添加新地址
|
||||
@POST("/api/addresses")
|
||||
Call<BaseResponse<Address>> addAddress(
|
||||
@Header("Authorization") String token,
|
||||
@Body Address address
|
||||
);
|
||||
|
||||
// 更新地址
|
||||
@PUT("/api/addresses/{id}")
|
||||
Call<BaseResponse<Address>> updateAddress(
|
||||
@Header("Authorization") String token,
|
||||
@Path("id") String addressId,
|
||||
@Body Address address
|
||||
);
|
||||
|
||||
// 删除地址
|
||||
@DELETE("/api/addresses/{id}")
|
||||
Call<BaseResponse> deleteAddress(
|
||||
@Header("Authorization") String token,
|
||||
@Path("id") String addressId
|
||||
);
|
||||
|
||||
// 设置默认地址
|
||||
@PUT("/api/addresses/{id}/default")
|
||||
Call<BaseResponse> setDefaultAddress(
|
||||
@Header("Authorization") String token,
|
||||
@Path("id") String addressId
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package com.startsmake.llrisetabbardemo.api;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.AsyncTask;
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
|
||||
public class TestNetworkConnection extends AsyncTask<Void, Void, String> {
|
||||
|
||||
private Context context;
|
||||
|
||||
public TestNetworkConnection(Context context) {
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(Void... voids) {
|
||||
String result = "";
|
||||
HttpURLConnection urlConnection = null;
|
||||
|
||||
try {
|
||||
// 测试服务器地址
|
||||
URL url = new URL(ApiClient.BASE_URL);
|
||||
urlConnection = (HttpURLConnection) url.openConnection();
|
||||
urlConnection.setConnectTimeout(10000); // 10秒连接超时
|
||||
urlConnection.setReadTimeout(10000); // 10秒读取超时
|
||||
urlConnection.setRequestMethod("GET");
|
||||
|
||||
int responseCode = urlConnection.getResponseCode();
|
||||
if (responseCode == HttpURLConnection.HTTP_OK) {
|
||||
// 读取响应
|
||||
InputStream inputStream = urlConnection.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
String line;
|
||||
|
||||
while ((line = reader.readLine()) != null) {
|
||||
stringBuilder.append(line);
|
||||
}
|
||||
|
||||
result = stringBuilder.toString();
|
||||
reader.close();
|
||||
inputStream.close();
|
||||
} else {
|
||||
result = "连接失败,响应码: " + responseCode;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
result = "连接异常: " + e.getMessage();
|
||||
} finally {
|
||||
if (urlConnection != null) {
|
||||
urlConnection.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
super.onPostExecute(result);
|
||||
Toast.makeText(context, "网络连接测试结果: " + result, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.startsmake.llrisetabbardemo.api.response;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class BaseResponse<T> {
|
||||
@SerializedName("status")
|
||||
private String status;
|
||||
|
||||
@SerializedName("message")
|
||||
private String message;
|
||||
|
||||
@SerializedName("data")
|
||||
private T data;
|
||||
|
||||
// Getters and Setters
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public T getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(T data) {
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public boolean isSuccess() {
|
||||
return "success".equals(status);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package com.startsmake.llrisetabbardemo.api.response;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class ChatMessageResponse {
|
||||
@SerializedName("id")
|
||||
private String id;
|
||||
|
||||
@SerializedName("senderId")
|
||||
private String senderId;
|
||||
|
||||
@SerializedName("receiverId")
|
||||
private String receiverId;
|
||||
|
||||
@SerializedName("content")
|
||||
private String content;
|
||||
|
||||
@SerializedName("time")
|
||||
private String time;
|
||||
|
||||
@SerializedName("read")
|
||||
private boolean read;
|
||||
|
||||
@SerializedName("type")
|
||||
private String type;
|
||||
|
||||
@SerializedName("senderName")
|
||||
private String senderName;
|
||||
|
||||
@SerializedName("receiverName")
|
||||
private String receiverName;
|
||||
|
||||
// Getters and Setters
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getSenderId() {
|
||||
return senderId;
|
||||
}
|
||||
|
||||
public void setSenderId(String senderId) {
|
||||
this.senderId = senderId;
|
||||
}
|
||||
|
||||
public String getReceiverId() {
|
||||
return receiverId;
|
||||
}
|
||||
|
||||
public void setReceiverId(String receiverId) {
|
||||
this.receiverId = receiverId;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getTime() {
|
||||
return time;
|
||||
}
|
||||
|
||||
public void setTime(String time) {
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
public void setRead(boolean read) {
|
||||
this.read = read;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getSenderName() {
|
||||
return senderName;
|
||||
}
|
||||
|
||||
public void setSenderName(String senderName) {
|
||||
this.senderName = senderName;
|
||||
}
|
||||
|
||||
public String getReceiverName() {
|
||||
return receiverName;
|
||||
}
|
||||
|
||||
public void setReceiverName(String receiverName) {
|
||||
this.receiverName = receiverName;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
package com.startsmake.llrisetabbardemo.api.response;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ProductResponse {
|
||||
@SerializedName("id")
|
||||
private String id;
|
||||
|
||||
@SerializedName("title")
|
||||
private String title;
|
||||
|
||||
@SerializedName("description")
|
||||
private String description;
|
||||
|
||||
@SerializedName("category")
|
||||
private String category;
|
||||
|
||||
@SerializedName("price")
|
||||
private double price;
|
||||
|
||||
@SerializedName("image_urls")
|
||||
private List<String> imageUrls;
|
||||
|
||||
@SerializedName("contact")
|
||||
private String contact;
|
||||
|
||||
@SerializedName("publish_time")
|
||||
private long publishTime;
|
||||
|
||||
@SerializedName("seller_id")
|
||||
private String sellerId;
|
||||
|
||||
// Getters and Setters
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
public void setDescription(String description) {
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public double getPrice() {
|
||||
return price;
|
||||
}
|
||||
|
||||
public void setPrice(double price) {
|
||||
this.price = price;
|
||||
}
|
||||
|
||||
public List<String> getImageUrls() {
|
||||
return imageUrls;
|
||||
}
|
||||
|
||||
public void setImageUrls(List<String> imageUrls) {
|
||||
this.imageUrls = imageUrls;
|
||||
}
|
||||
|
||||
public String getContact() {
|
||||
return contact;
|
||||
}
|
||||
|
||||
public void setContact(String contact) {
|
||||
this.contact = contact;
|
||||
}
|
||||
|
||||
public long getPublishTime() {
|
||||
return publishTime;
|
||||
}
|
||||
|
||||
public void setPublishTime(long publishTime) {
|
||||
this.publishTime = publishTime;
|
||||
}
|
||||
|
||||
public String getSellerId() {
|
||||
return sellerId;
|
||||
}
|
||||
|
||||
public void setSellerId(String sellerId) {
|
||||
this.sellerId = sellerId;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
package com.startsmake.llrisetabbardemo.api.response;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
public class UserResponse extends BaseResponse<UserResponse.UserInfo> {
|
||||
public static class UserInfo {
|
||||
@SerializedName("id")
|
||||
private String id;
|
||||
|
||||
@SerializedName("username")
|
||||
private String username;
|
||||
|
||||
@SerializedName("phone")
|
||||
private String phone;
|
||||
|
||||
@SerializedName("token")
|
||||
private String token;
|
||||
|
||||
@SerializedName("avatar")
|
||||
private String avatar;
|
||||
|
||||
@SerializedName("bio")
|
||||
private String bio;
|
||||
|
||||
@SerializedName("creditScore")
|
||||
private String creditScore;
|
||||
|
||||
@SerializedName("memberLevel")
|
||||
private String memberLevel;
|
||||
|
||||
@SerializedName("wantCount")
|
||||
private Integer wantCount;
|
||||
|
||||
@SerializedName("sellingCount")
|
||||
private Integer sellingCount;
|
||||
|
||||
@SerializedName("soldCount")
|
||||
private Integer soldCount;
|
||||
|
||||
@SerializedName("savedMoney")
|
||||
private String savedMoney;
|
||||
|
||||
// Getters and Setters
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
|
||||
public void setUsername(String username) {
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(String avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
public String getBio() {
|
||||
return bio;
|
||||
}
|
||||
|
||||
public void setBio(String bio) {
|
||||
this.bio = bio;
|
||||
}
|
||||
|
||||
public String getCreditScore() {
|
||||
return creditScore;
|
||||
}
|
||||
|
||||
public void setCreditScore(String creditScore) {
|
||||
this.creditScore = creditScore;
|
||||
}
|
||||
|
||||
public String getMemberLevel() {
|
||||
return memberLevel;
|
||||
}
|
||||
|
||||
public void setMemberLevel(String memberLevel) {
|
||||
this.memberLevel = memberLevel;
|
||||
}
|
||||
|
||||
public Integer getWantCount() {
|
||||
return wantCount;
|
||||
}
|
||||
|
||||
public void setWantCount(Integer wantCount) {
|
||||
this.wantCount = wantCount;
|
||||
}
|
||||
|
||||
public Integer getSellingCount() {
|
||||
return sellingCount;
|
||||
}
|
||||
|
||||
public void setSellingCount(Integer sellingCount) {
|
||||
this.sellingCount = sellingCount;
|
||||
}
|
||||
|
||||
public Integer getSoldCount() {
|
||||
return soldCount;
|
||||
}
|
||||
|
||||
public void setSoldCount(Integer soldCount) {
|
||||
this.soldCount = soldCount;
|
||||
}
|
||||
|
||||
public String getSavedMoney() {
|
||||
return savedMoney;
|
||||
}
|
||||
|
||||
public void setSavedMoney(String savedMoney) {
|
||||
this.savedMoney = savedMoney;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package com.startsmake.llrisetabbardemo.decoration;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.view.View;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
public class RoadItemDecoration extends RecyclerView.ItemDecoration {
|
||||
|
||||
private Paint roadPaint;
|
||||
private int roadWidth;
|
||||
|
||||
public RoadItemDecoration(Context context) {
|
||||
roadPaint = new Paint();
|
||||
roadPaint.setColor(Color.GRAY);
|
||||
roadPaint.setStyle(Paint.Style.FILL);
|
||||
roadWidth = dpToPx(context, 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
|
||||
@NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
|
||||
int position = parent.getChildAdapterPosition(view);
|
||||
int column = position % 2;
|
||||
|
||||
// 设置道路间距
|
||||
if (column == 0) {
|
||||
outRect.right = roadWidth / 2;
|
||||
} else {
|
||||
outRect.left = roadWidth / 2;
|
||||
}
|
||||
|
||||
// 行间道路
|
||||
if (position >= 2) {
|
||||
outRect.top = roadWidth;
|
||||
}
|
||||
}
|
||||
|
||||
private int dpToPx(Context context, int dp) {
|
||||
float density = context.getResources().getDisplayMetrics().density;
|
||||
return Math.round(dp * density);
|
||||
}
|
||||
}
|
||||
@ -1,24 +1,75 @@
|
||||
package com.startsmake.llrisetabbardemo.fragment;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import com.startsmake.llrisetabbardemo.R;
|
||||
import com.startsmake.llrisetabbardemo.activity.ChatActivity;
|
||||
import com.startsmake.llrisetabbardemo.adapter.MessageAdapter;
|
||||
import com.startsmake.llrisetabbardemo.model.MessageItem;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* User:Shine
|
||||
* Date:2015-10-20
|
||||
* Description:
|
||||
*/
|
||||
public class MessageFragment extends Fragment {
|
||||
|
||||
private RecyclerView rvMessageList;
|
||||
private MessageAdapter messageAdapter;
|
||||
private List<MessageItem> messageList;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.fragment_message, container, false);
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
View view = inflater.inflate(R.layout.fragment_message, container, false);
|
||||
initView(view);
|
||||
initData();
|
||||
return view;
|
||||
}
|
||||
|
||||
private void initView(View view) {
|
||||
rvMessageList = view.findViewById(R.id.rvMessageList);
|
||||
rvMessageList.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||
}
|
||||
|
||||
private void initData() {
|
||||
messageList = new ArrayList<>();
|
||||
|
||||
|
||||
// 添加测试账号到消息列表
|
||||
messageList.add(new MessageItem("测试账号1", "这是测试账号1的消息", "刚刚", 0, false));
|
||||
messageList.add(new MessageItem("测试账号2", "这是测试账号2的消息", "1小时前", 1, false));
|
||||
messageList.add(new MessageItem("测试账号3", "这是测试账号3的消息", "昨天", 0, false));
|
||||
|
||||
messageAdapter = new MessageAdapter(getContext(), messageList);
|
||||
rvMessageList.setAdapter(messageAdapter);
|
||||
|
||||
// 添加点击监听
|
||||
messageAdapter.setOnItemClickListener(new MessageAdapter.OnItemClickListener() {
|
||||
@Override
|
||||
public void onItemClick(MessageItem item) {
|
||||
// 跳转到聊天页面
|
||||
Intent intent = new Intent(getActivity(), ChatActivity.class);
|
||||
intent.putExtra("chat_title", item.getTitle());
|
||||
|
||||
// 根据标题设置对应的用户ID
|
||||
String userId = "";
|
||||
if (item.getTitle().equals("测试账号1")) {
|
||||
userId = "13800138001";
|
||||
} else if (item.getTitle().equals("测试账号2")) {
|
||||
userId = "13800138002";
|
||||
} else if (item.getTitle().equals("测试账号3")) {
|
||||
userId = "13800138003";
|
||||
}
|
||||
|
||||
intent.putExtra("chat_user_id", userId);
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||