0

I'm setting up a RecyclerView using an Adapter class to populate it with objects consisting of a TextView and an ImageView. The string that i want to use as my TextView's text is passed and saved in my Parcelable object , the PlaceObject class as a string resource. The problem is that although i have managed to access the string value through getString(placeObject.getName()) in my DetailsActivity class, i keep on getting a ResourceNotFoundException when i do Resources.getSystem().getString(event.name) in my LocalEventsAdapter's onBindViewHolder method. Any ideas?

I've read in other posts that you can use getString like in java to do the same operation but i can't make that work since the available getString methods that i saw in the docs don't accept ints as parameters. I also found the Resources.getSystem().getString() solution on another post but that didn't work either.

Here's my PlaceObject class extending the Parcelable interface:

class PlaceObject : Parcelable { // Implementing the Parcelable interface to allow for cleaner and faster code

private val TAG = PlaceObject::class.java.simpleName
private val baseMapSearchUrl = "https://www.google.com/maps/search/?api=1&query=" // Base url for launching a Map activity with a Search Intent

// Using int so that the values can be accessed via R.string etc.
var name: Int = 0
    private set
var description: Int = 0
    private set
var category: Int = 0
    private set
var locationDistance: String? = "location"
    private set
private var finalMapSearchUrl = baseMapSearchUrl

val mapSearchUrl: String
    get() {
        Log.d(TAG, "finalMapSearchUrl = $finalMapSearchUrl")
        return finalMapSearchUrl
    }

internal constructor(name: Int, description: Int, category: Int, locationDistance: String, mapUrlParam: String) {
    this.name = name
    this.description = description
    this.locationDistance = locationDistance
    this.category = category
    finalMapSearchUrl += Uri.encode(mapUrlParam)
}

private constructor(`in`: Parcel) {
    name = `in`.readInt()
    description = `in`.readInt()
    locationDistance = `in`.readString()
    category = `in`.readInt()
    finalMapSearchUrl = `in`.readString().toString()
}

override fun describeContents(): Int {
    return 0
}

override fun writeToParcel(parcel: Parcel, i: Int) {
    parcel.writeInt(name)
    parcel.writeInt(description)
    parcel.writeString(locationDistance)
    parcel.writeInt(category)
    parcel.writeString(finalMapSearchUrl)
}

companion object CREATOR : Parcelable.Creator<PlaceObject> {
    override fun createFromParcel(parcel: Parcel): PlaceObject {
        return PlaceObject(parcel)
    }

    override fun newArray(size: Int): Array<PlaceObject?> {
        return arrayOfNulls(size)
    }
}
}

Now below is my DetailsActivity class inside of which a PlaceObject instance is added to my MainActivity's bundle as a Parcelable and then as you can see, i retrieve it, assign it to the local placeObject variable and then inside of the initializeData() method i access the string resource using the line placeName.setText(getString(placeObject.getName()));. This seems to work fine and the resource id is passed from one class to another without issues.

public class DetailsActivity extends BaseActivity {

private static final String TAG = DetailsActivity.class.getSimpleName();
// Intent Parcelable key
private static final String PARCELABLE_KEY = "parcelable";

private PlaceObject placeObject;
private String placeCategory;

@BindView(R.id.details_toolbar)
Toolbar detailsToolbar;
@BindView(R.id.toolbar_title)
TextView toolbarTitle;
@BindView(R.id.show_in_map_button)
Button mapButton;
@BindView(R.id.place_image)
ImageView placeImg;
@BindView(R.id.place_name)
TextView placeName;
@BindView(R.id.place_description)
TextView placeDesc;
@BindView(R.id.place_location)
TextView placeLocation;

@OnClick(R.id.show_in_map_button) void openMap() {
    String mapSearchUrl = placeObject.getMapSearchUrl();
    Log.d(TAG,"mapSearchUri = [" + mapSearchUrl + "]");

    Uri gmIntentUri = Uri.parse(mapSearchUrl); // Create a Uri from a string. Use the result to create an Intent
    Intent mapIntent = new Intent(Intent.ACTION_VIEW,gmIntentUri);

    // Make the Intent explicit by settings the Google Maps package
    mapIntent.setPackage("com.google.android.apps.maps");

    // Verify that there is an app available to receive the intent (e.g: Google Maps)
    if(mapIntent.resolveActivity(getPackageManager()) != null) {
        startActivity(mapIntent); // if the result is non-null there is at least one app that can handle the intent
    } else {
        Log.d(TAG,"Error resolving Activity for map Intent in openMap(), [Uri = " + gmIntentUri + "].");
        Log.d(TAG,"mapIntent.resolveActivity() = " + mapIntent.resolveActivity(getPackageManager()));
    }
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    turnStatusBarTransparent(this);
    setContentView(R.layout.activity_details);
    ButterKnife.bind(this);

    // Toolbar-ActionBar related
    detailsToolbar.setNavigationIcon(R.drawable.baseline_arrow_back_ios_white_24); // setting the back button icon
    setSupportActionBar(detailsToolbar);
    ActionBar actionBar = getSupportActionBar();
    if (actionBar != null) {
        actionBar.setDisplayShowTitleEnabled(false);
    }
    toolbarTitle.setText(getResources().getString(R.string.details_page_title));

    detailsToolbar.setNavigationOnClickListener(view -> finish()); // When the back button is pressed, end the activity

    scaleButtonDrawable();

    getIntentData(getIntent());

    initializeData();

}

/**
 * Updates the layout's views with the right data
 */
private void initializeData() {
    placeName.setText(getString(placeObject.getName()));
    placeDesc.setText(getString(placeObject.getDescription()));
    placeLocation.setText(placeObject.getLocationDistance());
    placeCategory = getString(placeObject.getCategory());
    try {
        placeImg.setImageDrawable(determineImageResource());
    } catch (NullPointerException npe) {
        npe.printStackTrace();
        Log.d(TAG,"Null Pointer Exception in determineImageResource()");
    }
}

/**
 * Determines which image drawable to use for the
 * selected UI item based on it's name.
 * @return Returns the drawable resource for the image
 */
private Drawable determineImageResource() {
    if(placeCategory.equals(getString(R.string.beach_category))) {
        switch (getString(placeObject.getName())) {
            case "Spilia":
                return getResources().getDrawable(R.drawable.spilia,null);
            case "Plakes":
                return getResources().getDrawable(R.drawable.plakes,null);
            case "Bisti":
                return getResources().getDrawable(R.drawable.bisti,null);
            default:
                Log.d(TAG,"Error resolving Beach name in determineImageResource()");
                return getResources().getDrawable(R.drawable.beach_bg_placeholder,null);
        }
    } else if(placeCategory.equals(getString(R.string.museum_category))) {
        switch (getString(placeObject.getName())) {
            case "Historical Archives Museum":
                return getResources().getDrawable(R.drawable.historical_archives_museum,null);
            case "Ecclesiastical Museum":
                return getResources().getDrawable(R.drawable.ecclesiastical_museum,null);
                default:
                    Log.d(TAG,"Error resolving Museum name in determineImageResource()");
                    return getResources().getDrawable(R.drawable.beach_bg_placeholder,null);
        }
    } else if(placeCategory.equals(getString(R.string.event_category))) {
        switch (getString(placeObject.getName())) {
            case "Miaoulia":
                disableMapButton();
                return getResources().getDrawable(R.drawable.miaoulia_event,null);
            default:
                Log.d(TAG,"Error resolving Event name in determineImageResource()");
                return getResources().getDrawable(R.drawable.beach_bg_placeholder,null);
        }
    } else if(placeCategory.equals("Restaurant")) {
        switch (getString(placeObject.getName())) {
            case "Techne":
                return getResources().getDrawable(R.drawable.techne_restaurant,null);
            case "Xeri Elia":
                return getResources().getDrawable(R.drawable.xeri_elia_restaurant,null);
            case "Sunset":
                return getResources().getDrawable(R.drawable.sunset_restaurant,null);
            default:
                Log.d(TAG,"Error resolving Restaurant name in determineImageResource()");
                return getResources().getDrawable(R.drawable.beach_bg_placeholder,null);
        }
    } else {
        Log.d(TAG,"Couldn't determine imageResource in determineImageResource(). Using placeholder image.");
        return getResources().getDrawable(R.drawable.beach_bg_placeholder,null);
    }
}

/**
 * Hides the "Show In Map" button from the UI
 */
private void disableMapButton() {
    mapButton.setVisibility(View.GONE);
}

/**
 * Unpacks the data from the intent and assigns
 * it to the respective fields
 * @param intent The calling Intent
 */
private void getIntentData(Intent intent) {
    Bundle dataBundle = intent.getExtras();
    if (dataBundle != null) {
        placeObject = dataBundle.getParcelable(PARCELABLE_KEY);
    } else {
        Log.d(TAG,"Data bundle in getIntentData() is null");
    }
}

/**
 * Scales the "Show in Map" Button's drawable to fit
 * into the button correctly.
 */
private void scaleButtonDrawable() {
    // Scales down the button's drawable to fit the button
    Drawable drawable = getResources().getDrawable(R.drawable.location_btn,null);
    drawable.setBounds(new Rect(0,0,70,70));
    mapButton.setCompoundDrawables(drawable,null,null,null);
}
}

In my LocalEventsAdapter class though, when i try to do a similar thing, i get a ResourceNotFoundException thrown at me when i use Resources.getSystem().getString(event.name) inside of the onBindViewHolder() method. The localEvents val is simply an Array of PlaceObject objects so i don't think that is causing the issue.

class LocalEventsAdapter(private val localEvents : Array<PlaceObject>) :
        RecyclerView.Adapter<LocalEventsAdapter.LocalEventHolder>() {

private val TAG:String = "LocalEventsAdapter"

// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder.
class LocalEventHolder(view:View) : RecyclerView.ViewHolder(view) {
    private var v : View = view
    var img : ImageView? = view.findViewById(R.id.localEventImg)
    var txt : TextView? = view.findViewById(R.id.localEventTXV)
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocalEventHolder {
    // Create a new view
    val eventName = LayoutInflater.from(parent.context)
            .inflate(R.layout.local_event_rcv_item,parent,false)

    // set the view's size, margins, paddings and layout parameters
    // ...
    return LocalEventHolder(eventName)
}

override fun onBindViewHolder(holder: LocalEventHolder, pos: Int) {
    // get element from your dataset at this position
    // replace the contents of the view with that element
    val event : PlaceObject = localEvents[pos]
    val eventName : String = event.name.toString() //Resources.getSystem().getString(event.name)
    holder.txt!!.text = eventName//TODO:sp Above commented line throws ResourceNotFoundException
    holder.img?.setImageDrawable(determineImgRes(eventName,pos))
    if(holder.img == null) Log.d(TAG,"Image is null")
}

private fun determineImgRes(name:String, pos : Int): Drawable? {
    return when(name) {
        "Test Name" -> R.drawable.miaoulia_event.toDrawable()
        else -> R.drawable.local_events_placeholder.toDrawable() //TODO:sp Drawable isn't loading correctly, getting purple solid drawable instead
    }
}

// Return the size of your dataset (invoked by the layout manager)
override fun getItemCount() = localEvents.size
}

I expect that there is a similar method to getString() that can "decode" the int value of the event.name variable and retrieve the string resource associated with it but i can't seem to find the right one. What am i missing here?

Stelios Papamichail
  • 955
  • 2
  • 19
  • 57
  • 1
    `Resources.getSystem()` is not your app's `Resources`, but the system `Resources`. Your string resources are not to be found in the system `Resources`, which is why you get that Exception. You would need to call `getResources()` on a `Context` in your app. If you'd rather not explicitly pass a `Context` to your `Adapter`, you could get one from any `View`; e.g., `holder.txt.context`. However, if you're just setting that string resource directly on a `TextView`, you can simply call `setText()` with the `int` resource value, and the `TextView` will handle the resource lookup for you. – Mike M. Sep 17 '19 at 00:22
  • 1
    @MikeM. Wow, thank you so much. I didn't realize what i was doing. Thanks for the tips too! I ended up passing a Context to my adapter class – Stelios Papamichail Sep 17 '19 at 10:47

0 Answers0