添加一个新的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 解析为Fragmenticon 设置为int。 如果我添加新的drawable,那么它会选择随机drawables来显示解析的xml的图标。 我有一个Adapter 用于RecyclerListViewPojo 类和 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的图标的主要内容,如果未能解决你的问题,请参考以下文章

如何在xml中使用纯色形状后面的drawable添加阴影?

xml drawable

有没有办法改变按钮后面的背景颜色(button.setBackgroundResource)

Android点击按钮后改变颜色

可通过代码绘制的圆角

如何识别可绘制的 xml 文件?