添加相册集浏览窗口图片置顶功能
- 新增 PinnedImageDbHelper 数据库帮助类,存储图片置顶路径 - 使用独立数据库文件 pinned_image.db 存储图片置顶数据 - 相册集封面试图添加置顶/取消置顶菜单,长按弹出 - 置顶图片显示置顶图标并排在前面显示 - 相册集浏览窗口初始加载时按置顶排序
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
#Created by .winboll/winboll_app_build.gradle
|
#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
|
stageCount=4
|
||||||
libraryProject=
|
libraryProject=
|
||||||
baseVersion=15.0
|
baseVersion=15.0
|
||||||
publishVersion=15.0.3
|
publishVersion=15.0.3
|
||||||
buildCount=11
|
buildCount=22
|
||||||
baseBetaVersion=15.0.4
|
baseBetaVersion=15.0.4
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package cc.winboll.studio.gallery;
|
package cc.winboll.studio.gallery;
|
||||||
|
|
||||||
|
import android.content.DialogInterface;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -10,6 +11,8 @@ import androidx.annotation.NonNull;
|
|||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
|
||||||
import cc.winboll.studio.libappbase.LogUtils;
|
import cc.winboll.studio.libappbase.LogUtils;
|
||||||
|
|
||||||
@@ -20,6 +23,7 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
|
|||||||
private OnImageClickListener listener;
|
private OnImageClickListener listener;
|
||||||
private int bgType = 0;
|
private int bgType = 0;
|
||||||
private Preferences prefs;
|
private Preferences prefs;
|
||||||
|
private PinnedImageDbHelper pinnedDbHelper;
|
||||||
|
|
||||||
private int getBgRes() {
|
private int getBgRes() {
|
||||||
switch (bgType) {
|
switch (bgType) {
|
||||||
@@ -45,13 +49,60 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
|
|||||||
public void setData(ArrayList<Uri> urls, ArrayList<String> paths) {
|
public void setData(ArrayList<Uri> urls, ArrayList<String> paths) {
|
||||||
this.imageUrls = urls;
|
this.imageUrls = urls;
|
||||||
this.imagePaths = paths;
|
this.imagePaths = paths;
|
||||||
|
sortPinnedFirst();
|
||||||
LogUtils.d(TAG, "setData: " + urls.size() + " images");
|
LogUtils.d(TAG, "setData: " + urls.size() + " images");
|
||||||
notifyDataSetChanged();
|
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) {
|
public void setContext(android.content.Context context) {
|
||||||
prefs = new Preferences(context);
|
prefs = new Preferences(context);
|
||||||
bgType = prefs.getBgType();
|
bgType = prefs.getBgType();
|
||||||
|
pinnedDbHelper = PinnedImageDbHelper.getInstance(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshBg() {
|
public void refreshBg() {
|
||||||
@@ -60,7 +111,37 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
|
|||||||
notifyDataSetChanged();
|
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
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
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<Uri> urls = imageUrls;
|
||||||
final ArrayList<String> paths = imagePaths;
|
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
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
if (listener != null) {
|
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
|
@Override
|
||||||
@@ -97,9 +191,11 @@ public class ImageAdapter extends RecyclerView.Adapter<ImageAdapter.ViewHolder>
|
|||||||
|
|
||||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
ImageView imageView;
|
ImageView imageView;
|
||||||
|
ImageView pinIcon;
|
||||||
ViewHolder(View itemView) {
|
ViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
imageView = itemView.findViewById(R.id.image);
|
imageView = itemView.findViewById(R.id.image);
|
||||||
|
pinIcon = itemView.findViewById(R.id.pin_icon);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ import cc.winboll.studio.libappbase.LogUtils;
|
|||||||
|
|
||||||
public class PinnedAlbumDbHelper extends SQLiteOpenHelper {
|
public class PinnedAlbumDbHelper extends SQLiteOpenHelper {
|
||||||
public static final String TAG = "PinnedAlbumDbHelper";
|
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 int DB_VERSION = 1;
|
||||||
private static final String TABLE_NAME = "pinned_albums";
|
private static final String TABLE_NAME = "pinned_albums";
|
||||||
private static final String COLUMN_PATH = "album_path";
|
private static final String COLUMN_PATH = "album_path";
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,4 +12,15 @@
|
|||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:background="@color/black"/>
|
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>
|
</FrameLayout>
|
||||||
Reference in New Issue
Block a user