添加一个新的drawable它正在改变解析的xml的图标
Posted
技术标签:
【中文标题】添加一个新的drawable它正在改变解析的xml的图标【英文标题】:Adding a new drawable it is changing the icons of the parsed xml 【发布时间】:2019-09-23 01:02:51 【问题描述】:我遇到了一个问题,该问题仅在我添加新的可绘制对象时才会发生。
我将xml
解析为Fragment
,icon
设置为int
。
如果我添加新的drawable,那么它会选择随机drawables来显示解析的xml
的图标。
我有一个Adapter
用于RecyclerListView
。Pojo
类和 DB
扩展 SQLiteOpenHelper
。
如果我清除缓存和存储然后它恢复正常,
或者如果我删除新添加的可绘制对象,它会恢复正常。
有人可以帮助我了解更改图标的原因。
我试图清理项目并重建它。
使缓存无效并重新启动,但仍然相同。
下面你可以找到代码和代码下面两张图片的问题。
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
private static final int ITEM_TYPE_ONE = 0;
private static final int ITEM_TYPE_TWO = 1;
private final Handler handler = new Handler();
private final ArrayList<Bookmark> arrayList;
private final String BASE_URL = "https://besticon-demo.herokuapp.com/icon?url=";
private final Context context;
public MyAdapter(Context context, ArrayList<Bookmark> arrayList)
this.context = context;
this.arrayList = arrayList;
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
View view = null;
if (viewType == ITEM_TYPE_ONE)
view = LayoutInflater.from(context).inflate(R.layout.grid_item, parent, false);
return new ViewHolder(view);
else if (viewType == ITEM_TYPE_TWO)
view = LayoutInflater.from(context).inflate(R.layout.add_bookmark, parent, false);
return new ButtonViewHolder(view);
else
return null;
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position)
final int itemType = getItemViewType(position);
final Bookmark bookmark = this.arrayList.get(position);
if (itemType == ITEM_TYPE_ONE)
final ViewHolder viewHolder = (ViewHolder) holder;
RequestOptions requestOptions = new RequestOptions();
BookmarkDB bookmarkDB = new BookmarkDB(context);
String imageUrl = BASE_URL + arrayList.get(position).getSearchUrl() + "&size=32";
int resID = context.getResources().getIdentifier(String.valueOf(arrayList.get(position).getIcon()), "drawable", context.getPackageName());
if (resID == 0)
Glide.with(context)
.load(imageUrl)
.apply(requestOptions
.placeholder(R.drawable.default_favicon)
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.fitCenter())
.into(viewHolder.tvIcon);
else
viewHolder.tvIcon.setImageResource(resID);
String imageName = context.getResources().getResourceName(resID);
Log.d("getIcons", imageName); // This is the log.
else if (itemType == ITEM_TYPE_TWO)
ButtonViewHolder buttonViewHolder = (ButtonViewHolder) holder;
buttonViewHolder.imgButton.setImageResource(arrayList.get(position).getIcon());
class ViewHolder extends RecyclerView.ViewHolder
final ImageView tvIcon;
ViewHolder(@NonNull final View itemView)
super(itemView);
tvIcon = itemView.findViewById(R.id.image_view);
Bookmark.db
public class BookmarkDB extends SQLiteOpenHelper
private static final String DBNAME = "bookmarks.db"; // The name of the database file
private static final int DBVERSION = 1; // The Database version
public static final String TBL_BOOKMARK = "bookmark";
private static final String COL_ID = BaseColumns._ID; // equates to _id
private static final String COl_NAME = "name";
private static final String COl_HIDDEN = "hidden";
private static final String COL_ICON = "icon";
private static final String COL_NATIVEURL = "nativeurl";
private static final String COL_SEARCHURL = "searchurl";
private final SQLiteDatabase mDB;
Context mContext;
public BookmarkDB(Context context)
super(context, DBNAME, null, DBVERSION);
mDB = this.getWritableDatabase();
@Override
public void onCreate(SQLiteDatabase db)
// The SQL to be used to create the table
String crt_bookmark_tbl_sql = "CREATE TABLE IF NOT EXISTS " + TBL_BOOKMARK + "(" +
COL_ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
COl_NAME + " TEXT, " +
COl_HIDDEN + " INTEGER, " +
COL_ICON + " TEXT, " +
COL_NATIVEURL + " TEXT," +
COL_SEARCHURL + " TEXT" +
")";
db.execSQL(crt_bookmark_tbl_sql); // CREATE THE TABLE
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
db.execSQL("DROP IF TABLE EXISTS " + DBNAME);
onCreate(db);
public void updateName(String newName, int id, String oldName)
SQLiteDatabase db = this.getWritableDatabase();
String query = "UPDATE " + TBL_BOOKMARK + " SET " + COl_NAME +
" = '" + newName + "' WHERE " + COL_ID + " = '" + id + "'" +
" AND " + COl_NAME + " = '" + oldName + "'";
db.execSQL(query);
public void addBookmark(long id, String name, boolean hidden, String icon, String nativeurl, String searchurl)
ContentValues cv = new ContentValues();
cv.put(COl_HIDDEN, hidden);
cv.put(COl_NAME, name);
cv.put(COL_ICON, icon);
cv.put(COL_NATIVEURL, nativeurl);
cv.put(COL_SEARCHURL, searchurl);
mDB.insert(TBL_BOOKMARK, null, cv);
// uses the convenience insert method that builds the SQL
public ArrayList<Bookmark> getAllBookmarks()
ArrayList<Bookmark> rv = new ArrayList<>();
Cursor csr = mDB.query(TBL_BOOKMARK, null, null, null, null, null, null);
while (csr.moveToNext())
Bookmark b = new Bookmark();
b.setId(csr.getString(csr.getColumnIndex(COL_ID)));
int Icon = csr.getInt(csr.getColumnIndex(COL_ICON));
String name = csr.getString(csr.getColumnIndex(COl_NAME));
String searchUrl = csr.getString(csr.getColumnIndex(COL_SEARCHURL));
b.setIcon(Icon);
b.setName(name);
b.setSearchUrl(searchUrl);
b.setViewType(csr.getInt(csr.getColumnIndex(COl_NAME)));
b.setNativeUrl(csr.getString(csr.getColumnIndex(COL_NATIVEURL)));
rv.add(b);
return rv;
这是.XML
文件。
<?xml version="1.0" encoding="utf-8"?>
<Bookmarks>
<Bookmark name="Bing"
hidden="true"
icon="bing"
id="0"
nativeUrl=""
searchUrl="https://www.bing.com" />
<Bookmark
name="Google"
hidden="true"
icon="google"
id="1"
nativeUrl=""
searchUrl="https://www.google.com" />
<Bookmark
name="Youtube"
hidden="false"
icon="youtube"
id="2"
nativeUrl="youtube://"
searchUrl="https://m.youtube.com" />
<Bookmark
name="Facebook"
hidden="false"
icon="facebook"
id="3"
nativeUrl="facebook://"
searchUrl="https://m.facebook.com" />
<Bookmark
name="Twitter"
hidden="false"
icon="twitter"
id="4"
nativeUrl=""
searchUrl="https://mobile.twitter.com/" />
</Bookmarks>
RecyclerView
的片段
public class FragmentBookmark extends Fragment
private final ArrayList<Bookmark> arrayList = new ArrayList<>();
private MyAdapter myAdapter;
private View paramView;
private RecyclerView myRecyclerView;
private BookmarkDB mDB;
private Context mContext;
@Override
public void onAttach(Context context)
super.onAttach(context);
mContext = context;
mDB = new BookmarkDB(mContext);
mDB.getAllBookmarks();
buildBookmarkArrayListfromDB();
loadBookMarksFromXML();
@Nullable
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState)
paramView = inflater.inflate(R.layout.bookmark, container, false);
myRecyclerView = paramView.findViewById(R.id.myRecyclerView);
// myRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
myRecyclerView.setLayoutManager(new GridLayoutManager(mContext, 4));
myRecyclerView.setHasFixedSize(true);
myAdapter = new MyAdapter(mContext, arrayList);
myRecyclerView.setAdapter(myAdapter);
myAdapter.notifyDataSetChanged();
Bookmark bookmark = new Bookmark();
bookmark.setViewType(1);
bookmark.setIcon(R.drawable.add_new_bookmark_icon);
arrayList.add(bookmark);
((MainActivity) getActivity()).setFragmentBookmarkListener(new MainActivity.FragmentBookmarkListener()
@Override
public void onRefresh()
assert getFragmentManager() != null;
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.detach(FragmentBookmark.this).attach(FragmentBookmark.this).commit();
);
return paramView;
private void loadBookMarksFromXML()
// MAY WISH TO ONLY DO THIS ONCE as bookmarks would be loaded OTHERWISE DELETE LINE BELOW
if (DatabaseUtils.queryNumEntries(mDB.getWritableDatabase(), BookmarkDB.TBL_BOOKMARK) > 0)
return;
try
XmlResourceParser xpp = getResources().getXml(R.xml.bookmarks);
while (xpp.getEventType() != XmlPullParser.END_DOCUMENT)
if (xpp.getEventType() == XmlPullParser.START_TAG)
if (xpp.getName().equals("Bookmark"))
Bookmark bookmark = new Bookmark();
bookmark.setName(xpp.getAttributeValue(null, "name"));
bookmark.setSearchUrl(xpp.getAttributeValue(null, "searchUrl"));
bookmark.setNativeUrl(xpp.getAttributeValue(null, "nativeUrl"));
bookmark.setId(xpp.getAttributeValue(null, "id"));
int drawableResourceId = getResources().getIdentifier(xpp.getAttributeValue(null, "icon"), "drawable", mContext.getPackageName());
bookmark.setIcon(drawableResourceId);
bookmark.setViewType(0);
if (bookmark.getId() == null)
bookmark.setId("1");
mDB.addBookmark(
Long.valueOf(bookmark.getId()),
bookmark.getName(),
bookmark.getViewType() > 0,
String.valueOf(bookmark.getIcon()),
bookmark.getNativeUrl(),
bookmark.getSearchUrl()
);
xpp.next();
catch (XmlPullParserException e)
e.printStackTrace();
catch (IOException e)
e.printStackTrace();
private void buildBookmarkArrayListfromDB()
arrayList.clear();
arrayList.addAll(mDB.getAllBookmarks());
Bookmark bookmark = new Bookmark();
bookmark.setViewType(1);
bookmark.setIcon(R.drawable.add_new_bookmark_icon);
arrayList.add(bookmark);
这是Pojo.class
public class Bookmark implements Parcelable, Comparable, Comparator<Bookmark>
public static final Creator<Bookmark> CREATOR = new Creator<Bookmark>()
@Override
public Bookmark createFromParcel(Parcel in)
return new Bookmark(in);
@Override
public Bookmark[] newArray(int size)
return new Bookmark[size];
;
private String name;
private String id;
private String nativeUrl;
private String searchUrl;
private String hidden;
private long db_id;
private int icon;
private int viewType;
private Bookmark(Parcel in)
name = in.readString();
id = in.readString();
nativeUrl = in.readString();
searchUrl = in.readString();
db_id = in.readLong();
icon = in.readInt();
viewType = in.readInt();
hidden = in.readString();
public Bookmark()
public String getName()
return name;
public void setName(String name)
this.name = name;
public int getIcon()
return icon;
public void setIcon(int icon)
this.icon = icon;
public String getId()
return id;
public void setId(String id)
this.id = id;
this.db_id = Integer.parseInt(id);
public String getNativeUrl()
return nativeUrl;
public void setNativeUrl(String nativeUrl)
this.nativeUrl = nativeUrl;
public String getSearchUrl()
return searchUrl;
public void setSearchUrl(String searchUrl)
this.searchUrl = searchUrl;
public int getViewType()
return viewType;
public void setViewType(int viewType)
this.viewType = viewType;
public String getHidden()
return hidden;
public void setHidden(String hidden)
this.hidden = hidden;
@Override
public String toString()
return "Bookmark" +
"name='" + name + '\'' +
", id='" + id + '\'' +
", nativeUrl='" + nativeUrl + '\'' +
", searchUrl='" + searchUrl + '\'' +
", hidden='" + hidden + '\'' +
", db_id=" + db_id +
", icon=" + icon +
", viewType=" + viewType +
'';
@Override
public int compareTo(Object o)
return 0;
@Override
public int describeContents()
return 0;
@Override
public void writeToParcel(Parcel dest, int flags)
dest.writeString(name);
dest.writeString(id);
dest.writeString(nativeUrl);
dest.writeString(searchUrl);
dest.writeLong(db_id);
dest.writeInt(icon);
dest.writeInt(viewType);
dest.writeString(hidden);
@Override
public int compare(Bookmark o1, Bookmark o2)
return 0;
这是Fragment
的布局。
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_
android:layout_
android:overScrollMode="never">
<android.support.v7.widget.RecyclerView
android:id="@+id/myRecyclerView"
android:layout_
android:layout_
android:fillViewport="false"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
</android.support.v7.widget.RecyclerView>
</android.support.v4.widget.NestedScrollView>
我创建了一个 LogCat 来显示结果。
正确的方式
drawable/youtube
drawable/facebook
drawable/twitter
添加可绘制或矢量资产或图像后
drawable/wiki
drawable/facebook
drawable/trash
【问题讨论】:
如果我正确地遵循了您的描述,那是因为您将图标作为它们的数字资源 ID 存储在数据库中。当您在资源中添加(或删除或更改)drawable,然后重新构建时,drawable 很可能不会具有与以前相同的数字 ID。最简单的解决方法可能是只存储图标的名称,而不是它们的数字 ID。 @MikeM。是的,你是对的,图标作为 int 保存在数据库中。你能回答你所说的最简单的解决方法吗? 【参考方案1】:您正在调用String.valueOf(bookmark.getIcon())
将图标存储在您的数据库中。由于图标是代表资源的int
,因此它将资源ID 存储为字符串。现在的问题是资源 ID 不稳定,并且可能会在您创建新 APK 时随时更改。因此,当您不更新应用程序时它可以工作,但之后开始失败。
相反,您应该存储资源的名称并保持它们稳定。您已经在您的 XML 数据中使用了该名称,所以无论如何我认为这是您的目标。
mDB.addBookmark(
Long.valueOf(bookmark.getId()),
bookmark.getName(),
bookmark.getViewType() > 0,
iconName(bookmark.getIcon()),
bookmark.getNativeUrl(),
bookmark.getSearchUrl()
);
String Icon = csr.getString(csr.getColumnIndex(COL_ICON));
b.setIcon(iconRes(Icon));
现在只需要实现name到id的映射即可。
String iconName(int icon)
return getResources().getResourceEntryName(icon);
int iconRes(String icon)
return getResources().getIdentifier(icon, "drawable", mContext.getPackageName())
您还需要在分离时取消设置mContext
或不存储它并改用getContext()
或requireContext()
。当然,如果您在某些后台操作中使用它,则必须检查 null。
@Override
public void onDetach()
mContext = null;
super.onDetach();
【讨论】:
【参考方案2】:尝试在您的 arrayList 在 FragmentBookmark 中添加工作之后执行myAdapter.notifyDataSetChanged();
:
Bookmark bookmark = new Bookmark();
bookmark.setViewType(1);
bookmark.setIcon(R.drawable.add_new_bookmark_icon);
arrayList.add(bookmark);
myAdapter.notifyDataSetChanged();
顺便说一句:xml 解析工作和数据库操作通常应该异步处理。
更新:将Bookmark中的icon
字段类型由int改为String,并修改loadBookMarksFromXML
中的部分代码,from
int drawableResourceId =
getResources().getIdentifier(
xpp.getAttributeValue(null, "icon"), "drawable",mContext.getPackageName());
bookmark.setIcon(drawableResourceId);
到
bookmark.setIcon(xpp.getAttributeValue(null, "icon"));
在MyAdapter
类中更改onBindViewHolder
中的一些代码,来自:
int resID = context.getResources().getIdentifier(String.valueOf(arrayList.get(position).getIcon()), "drawable", context.getPackageName());
到
int resID = context.getResources().getIdentifier(arrayList.get(position).getIcon()), "drawable", context.getPackageName());
您可以轻松修复因更改 icon
字段的类型而导致的其他错误并再次运行,让我知道结果。谢谢。
【讨论】:
同样的问题。 您提供的代码看起来不完整。请提供完整代码。 我添加了更多代码,包括Pojo.class
我忘记添加了,为此我添加了完整的 DB
。
"如果我添加新的可绘制对象,那么它会选择随机可绘制对象来显示已解析 xml 的图标。"我看到有两个地方你在 FragmentBookmark 中添加了新的书签图标项,一个在 buildBookmarkArrayListfromDB 方法中,另一个在 onCreateView 中,你是指哪一个?
添加到 FragmentBookmark 的书签图标是第二个视图,只有加号按钮。【参考方案3】:
为了将图像存储到 sqlite 或任何其他本地数据库,您必须存储变量的名称(我在使用本地数据库时这样做,也许有更好的方法)
所以当你想存储drawable时
使用此代码
bookmark.setIcon("add_new_bookmark_icon");//don't forget to change icon to String in bookmark
而不是这个
bookmark.setIcon(R.drawable.add_new_bookmark_icon);//you use this in onViewCreate and buildBookmarkArrayListfromDB
现在,当您想从数据库中显示可绘制对象时,请使用此代码
int resourceId = getResources().getIdentifier("", "drawable", getPackageName());
Drawable yourImage = ContextCompat.getDrawable(this,resourceId);
imageView.setImageDrawable(yourImage);
注意
您必须更改所有使用 setIcon 或 getIcon 的地方,以及您使用图标作为 int 的所有代码,如 getAllBookmarks
-> csr.getInt(csr.getColumnIndex(COL_ICON)
等
我几年前就这样做了,但我不检查它是否仍然有效。试试看,告诉我会发生什么
【讨论】:
你的意思是把 all 从 int 改成 String ? @Spritzig 是的,但我认为如果用我的代码替换 bookmark.setIcon 你的问题会解决但不确定 我在FragmentBookmark
确实有问题bookmark.setIcon()
它返回此错误setIcon (java.lang.String) in Bookmark cannot be applied to (android.graphics.drawable.Drawable)
不要将drawable发送到书签!只是当你想将它显示给 imageView 时使用 drawable。为此,您必须将可绘制图像的名称发布到 setIcon。我不知道您必须将什么图标发布到书签
使用这个书签.setIcon("add_new_bookmark_icon");【参考方案4】:
由于每次安装或生成 apk 时资源 ID 都会不断变化,因此明智的做法是存储图标资源的名称并在显示期间获取它。为此,您可以存储图标名称的字符串值(您也可以使用与书签相同的名称,但请确保可绘制的名称相同)。 在数据库 (Bookmark.db) 和 Pojo 中将图标字段更改为字符串。 在您的 RecyclerView 片段中删除此行
int drawableResourceId = getResources().getIdentifier(xpp.getAttributeValue(null, "icon"), "drawable", mContext.getPackageName());
更改下一行
bookmark.setIcon(xpp.getAttributeValue(null, "name"));
在 MyAdapter 类的 onBindViewHolder 中使用获取图标资源
int iresourceid = getResources().getIdentifier(arrayList.get(position).getIcon(),"drawable",getPackageName());
【讨论】:
【参考方案5】:也许你应该将图标从 res 文件夹移动到 assets,然后用 Glide 加载它。
if (getResources().getAssets().list("").contains("icon.png"))
Glide.with(fragment)
.load(Uri.parse("file:///android_asset/icon.png"))
.into(imageView);
else
// load from web
【讨论】:
以上是关于添加一个新的drawable它正在改变解析的xml的图标的主要内容,如果未能解决你的问题,请参考以下文章