添加相册集浏览窗口图片置顶功能

- 新增 PinnedImageDbHelper 数据库帮助类,存储图片置顶路径
- 使用独立数据库文件 pinned_image.db 存储图片置顶数据
- 相册集封面试图添加置顶/取消置顶菜单,长按弹出
- 置顶图片显示置顶图标并排在前面显示
- 相册集浏览窗口初始加载时按置顶排序
This commit is contained in:
2026-04-26 09:46:02 +08:00
parent 4d0d8c4d59
commit 4a267d5606
5 changed files with 190 additions and 5 deletions

View File

@@ -1,8 +1,8 @@
#Created by .winboll/winboll_app_build.gradle
#Sun Apr 26 09:00:16 CST 2026
#Sun Apr 26 09:40:02 CST 2026
stageCount=4
libraryProject=
baseVersion=15.0
publishVersion=15.0.3
buildCount=11
buildCount=22
baseBetaVersion=15.0.4

View File

@@ -1,5 +1,6 @@
package cc.winboll.studio.gallery;
import android.content.DialogInterface;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
@@ -10,6 +11,8 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import cc.winboll.studio.libappbase.LogUtils;
@@ -20,6 +23,7 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
private OnImageClickListener listener;
private int bgType = 0;
private Preferences prefs;
private PinnedImageDbHelper pinnedDbHelper;
private int getBgRes() {
switch (bgType) {
@@ -45,13 +49,60 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
public void setData(ArrayList<Uri> urls, ArrayList<String> paths) {
this.imageUrls = urls;
this.imagePaths = paths;
sortPinnedFirst();
LogUtils.d(TAG, "setData: " + urls.size() + " images");
notifyDataSetChanged();
}
private void sortPinnedFirst() {
if (pinnedDbHelper == null || imagePaths.isEmpty()) {
return;
}
ArrayList<String> pinnedPaths = new ArrayList<>();
ArrayList<String> unpinnedPaths = new ArrayList<>();
for (String path : imagePaths) {
if (pinnedDbHelper.isPinned(path)) {
pinnedPaths.add(path);
} else {
unpinnedPaths.add(path);
}
}
Comparator<String> pathComparator = new Comparator<String>() {
@Override
public int compare(String p1, String p2) {
return p1.compareToIgnoreCase(p2);
}
};
Collections.sort(pinnedPaths, pathComparator);
Collections.sort(unpinnedPaths, pathComparator);
ArrayList<Uri> newUrls = new ArrayList<>();
ArrayList<String> newPaths = new ArrayList<>();
for (String path : pinnedPaths) {
int index = imagePaths.indexOf(path);
if (index >= 0) {
newUrls.add(imageUrls.get(index));
newPaths.add(path);
}
}
for (String path : unpinnedPaths) {
int index = imagePaths.indexOf(path);
if (index >= 0) {
newUrls.add(imageUrls.get(index));
newPaths.add(path);
}
}
imageUrls.clear();
imagePaths.clear();
imageUrls.addAll(newUrls);
imagePaths.addAll(newPaths);
}
public void setContext(android.content.Context context) {
prefs = new Preferences(context);
bgType = prefs.getBgType();
pinnedDbHelper = PinnedImageDbHelper.getInstance(context);
}
public void refreshBg() {
@@ -60,7 +111,37 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
notifyDataSetChanged();
}
}
public void refreshPinned() {
if (pinnedDbHelper == null || imagePaths.isEmpty()) {
return;
}
sortPinnedFirst();
notifyDataSetChanged();
}
private void showContextMenu(View view, final int position) {
final String imagePath = imagePaths.get(position);
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(view.getContext());
builder.setTitle("Image");
boolean isPinned = pinnedDbHelper != null && pinnedDbHelper.isPinned(imagePath);
String[] items = isPinned ? new String[]{"取消置顶"} : new String[]{"置顶"};
builder.setItems(items, new android.content.DialogInterface.OnClickListener() {
@Override
public void onClick(android.content.DialogInterface dialog, int which) {
if (pinnedDbHelper != null && which == 0) {
if (isPinned) {
pinnedDbHelper.unpinImage(imagePath);
} else {
pinnedDbHelper.pinImage(imagePath);
}
refreshPinned();
}
}
});
builder.show();
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
@@ -80,7 +161,12 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
final ArrayList<Uri> urls = imageUrls;
final ArrayList<String> paths = imagePaths;
holder.imageView.setOnClickListener(new OnClickListener() {
final String imagePath = imagePaths.get(position);
boolean isPinned = pinnedDbHelper != null && pinnedDbHelper.isPinned(imagePath);
holder.pinIcon.setVisibility(isPinned ? View.VISIBLE : View.GONE);
holder.itemView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
@@ -88,6 +174,14 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
}
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
showContextMenu(holder.itemView, position);
return true;
}
});
}
@Override
@@ -97,9 +191,11 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView imageView;
ImageView pinIcon;
ViewHolder(View itemView) {
super(itemView);
imageView = itemView.findViewById(R.id.image);
pinIcon = itemView.findViewById(R.id.pin_icon);
}
}
}

View File

@@ -9,7 +9,7 @@ import cc.winboll.studio.libappbase.LogUtils;
public class PinnedAlbumDbHelper extends SQLiteOpenHelper {
public static final String TAG = "PinnedAlbumDbHelper";
private static final String DB_NAME = "gallery.db";
private static final String DB_NAME = "pinned_album.db";
private static final int DB_VERSION = 1;
private static final String TABLE_NAME = "pinned_albums";
private static final String COLUMN_PATH = "album_path";

View File

@@ -0,0 +1,78 @@
package cc.winboll.studio.gallery;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import cc.winboll.studio.libappbase.LogUtils;
public class PinnedImageDbHelper extends SQLiteOpenHelper {
public static final String TAG = "PinnedImageDbHelper";
private static final String DB_NAME = "pinned_image.db";
private static final int DB_VERSION = 1;
private static final String TABLE_NAME = "pinned_images";
private static final String COLUMN_PATH = "image_path";
private static final String SQL_CREATE = "CREATE TABLE " + TABLE_NAME + " ("
+ COLUMN_PATH + " TEXT PRIMARY KEY)";
private static PinnedImageDbHelper dbHelper;
public static PinnedImageDbHelper getInstance(Context context) {
if (dbHelper == null) {
dbHelper = new PinnedImageDbHelper(context.getApplicationContext());
}
return dbHelper;
}
public PinnedImageDbHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
onCreate(db);
}
public void pinImage(String imagePath) {
SQLiteDatabase db = getWritableDatabase();
ContentValues values = new ContentValues();
values.put(COLUMN_PATH, imagePath);
db.insertWithOnConflict(TABLE_NAME, null, values, SQLiteDatabase.CONFLICT_IGNORE);
LogUtils.d(TAG, "pinImage: " + imagePath);
}
public void unpinImage(String imagePath) {
SQLiteDatabase db = getWritableDatabase();
db.delete(TABLE_NAME, COLUMN_PATH + " = ?", new String[]{imagePath});
LogUtils.d(TAG, "unpinImage: " + imagePath);
}
public boolean isPinned(String imagePath) {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, null, COLUMN_PATH + " = ?",
new String[]{imagePath}, null, null, null);
boolean pinned = cursor.getCount() > 0;
cursor.close();
return pinned;
}
public String[] getPinnedPaths() {
SQLiteDatabase db = getReadableDatabase();
Cursor cursor = db.query(TABLE_NAME, new String[]{COLUMN_PATH}, null, null, null, null, null);
String[] paths = new String[cursor.getCount()];
int i = 0;
while (cursor.moveToNext()) {
paths[i++] = cursor.getString(0);
}
cursor.close();
return paths;
}
}

View File

@@ -12,4 +12,15 @@
android:scaleType="centerCrop"
android:background="@color/black"/>
<ImageView
android:id="@+id/pin_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="top|end"
android:layout_margin="4dp"
android:background="@android:color/transparent"
android:padding="3dp"
android:src="@drawable/ic_pin"
android:visibility="gone"/>
</FrameLayout>