Android - ResourcesNotFoundException - android

I am passing my custom object sub which has a color property from an activity to another and retrieving it like this:
val intent = this.intent
val bundle = intent.extras
sub = bundle.getParcelable("selected")
then, when a button is pressed a color picker shows up and lets me select a color, I have this method that listens for the color selection:
override fun onColorSelected(dialogId: Int, color: Int) {
sub.color = color
createsub_rel.backgroundColor = color
}
as you can see the color is returned as an Int.
The Exceptions happens in the onBindViewHolder() of my RecyclerView, specifically on this line:
viewHolder.relativeLayout.setBackgroundColor(mContext.getColor(sub.color))
the log states:
android.content.res.Resources$NotFoundException: Resource ID #0x7fff9800
I have debugged it and sub.color is actually the expected value, I have looked here on SO for a solution, specifically on this, but I couldn't find any working answer.

Related

how to assign a condition to an imageView by using if else?

I tried to show a random imageView out of 2(one,two) with this
binding.imageView.setImageResource(oneandtwo[random.nextInt(oneandtwo.size)]) it works fine
and
i wanted to increase score when i clicked on imageView
but score increases independent to that, sometimes increases when i clicked on imageView2 and sometimes imageView, i want to increase score when i only clicked on imageView. i couldnt figure out. Thanks in advance.
var score = 0
val oneandtwo: IntArray = intArrayOf(
R.drawable.ic_baseline_looks_one_24,
R.drawable.ic_baseline_looks_two_24
)
binding.imageView.setOnClickListener{
val random = Random
binding.imageView.setImageResource(oneandtwo[random.nextInt(oneandtwo.size)])
if (oneandtwo[random.nextInt(oneandtwo.size)]==(R.drawable.ic_baseline_looks_one_24)){
score++
binding.textView.text = score.toString()
}
}
The number you gave in the image and the number you checked in the if block may not match and will not give the result you want. If you change code like this. Probably your problem will be solved.
binding.imageView.setOnClickListener{
val random = Random().nextInt(oneandtwo.size)
binding.imageView.setImageResource(oneandtwo[random])
if (oneandtwo[random]==(R.drawable.ic_baseline_looks_one_24)){
score++
binding.textView.text = score.toString()
}
}
What you are doing is checking the resourceId of the newly generated image, not the one you just clicked. That's why it not giving the result you want ,i.e, increment on the click of imageView and not on click of imageView2. Try below code. it should work
var score = 0
val oneandtwo: IntArray = intArrayOf(R.drawable.ic_baseline_looks_one_24,R.drawable.ic_baseline_looks_two_24)
/*Initalize the initial image and tag either here or in xml file*/
val random = Random().nextInt(oneandtwo.size)
binding.imageView.setImageResource(oneandtwo[random])
binding.imageView.Tag = oneandtwo[random]
binding.imageView.setOnClickListener{
val imageTag = binding.imageView.Tag
if (imageTag == (R.drawable.ic_baseline_looks_one_24)) {
score++
binding.textView.text = score.toString()
}
val random = Random().nextInt(oneandtwo.size)
binding.imageView.setImageResource(oneandtwo[random])
binding.imageView.Tag = oneandtwo[random]
}
You've got two problems that both the answers cover - if clicking a particular image is meant to give you points, you have to check the image before you change it. And if you're using random items, you need to pick one and keep a reference to it. This:
binding.imageView.setImageResource(oneandtwo[random.nextInt(oneandtwo.size)])
if (oneandtwo[random.nextInt(oneandtwo.size)]==(R.drawable.ic_baseline_looks_one_24))
picks two completely independent numbers which may not match - and they're supposed to be referencing the same item, right? Get your random thing once, use it twice
RahulK's answer should work but here's another way you could do it, with an explicit listener object so you can throw a state variable in there:
binding.imageView.setOnClickListener(object : View.OnClickListener {
// keep track of whether the current image adds to the score when clicked
var givesPoints = false
override fun onClick(view: View) {
// first, we just got clicked, so add to the score if appropriate
if (givesPoints) score++
// you can just call random() on a collection to get a random element from it
val resId = oneAndTwo.random()
// set the image - might be better to do (view as ImageView).setImageResource
// so it sets it on -whatever was clicked- so it's easier to reuse
binding.imageView.setImageResource(resId)
// now set whether this new image gives points or not
givesPoints = resId == R.drawable.ic_baseline_looks_one_24
}
})
So this way, every time you set a new image, the listener knows whether to give points for it next time it's clicked
I don't know how you have this set up, you're only initialising things when the image is clicked so if you need to set them up beforehand (so you can have an image displayed that you an click for points) you probably want everything in a separate function you can call when clicked and during setup:
/** Assigns a random picture to this ImageView - returns true if it's a point-scoring pic */
fun assignRandomPic(imageView: Imageview): Boolean {
val resId = oneAndTwo.random()
imageView.setImageResource(resId)
return resId == R.drawable.ic_baseline_looks_one_24
}
// set an initial image, storing whether it scores points
val scoreMe = assignRandomPic(binding.imageView)
binding.imageView.setOnClickListener(object : View.OnClickListener {
// initialise this as appropriate for the image we just set up
var givesPoints = scoreMe
override fun onClick(view: View) {
if (givesPoints) score++
// set a new pic and store its point-scoring state
givesPoints = assignRandomPic(view as ImageView)
}
})
or you could just do var givesPoints = assignRandomPic(binding.imageView) and init the image inside the click listener, whatever feels better

Android Kotlin: Setting fragment TextView background on start from ViewModel

I am confused by a certain inconsistency in my code, where only part of the data is loading. I am trying to set up a grid of TextViews in my fragment, which read from a list variable called board on the ViewModel for that fragment. The TextView text is set as board[n].text from the view model, where n is its index in the list, and this loads just fine. I am also trying to set the TextView background to one of three background resources, which are saved as an int board[n].marking on the view model.
This does not work. It seems that it is trying to load the background for each TextView before board has been fully initialized in the view model, but it does not seem to try to do the same for the TextView text. Here are the relevant parts of my code. First, the XML layout:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".screens.game.GameFragment">
<data>
<import type="android.view.View"/>
<variable
name="gameViewModel"
type="com.example.mygametitle.screens.game.GameViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
(...)
<TextView
android:id="#+id/field13"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="#dimen/board_vertical_margin"
android:background="#{gameViewModel.board[2].marking}"
android:onClick="#{ () -> gameViewModel.openGameDialog(2, field13)}"
android:text="#{gameViewModel.board[2].text}"
(...)
There are 25 fields like that. All of the text loads properly, but none of the background images load. If instead I hardcode the background I want, as such, it loads properly:
android:background="#drawable/board_fieldbackground_checked" . This won't work for me though, as I need to read what each entry's background is upon startup--they don't all start checked.
On the view model, board is made by reading a set of 25 entries from a Room database, each including (among other info) a text string and a marking int. These all update properly--if I use a debug function to print out the contents of my board, they all have the proper text and marking upon closing and reopening the fragment. When the fragment opens, all the text is correct, but the backgrounds are not. Any ideas on why my backgrounds aren't loading the same way the text is?
Here's some of the relevant viewmodel code:
class GameViewModel(
val database: BoardDatabaseDao,
application: Application,
val boardTitle: String) : AndroidViewModel(application) {
val BG_UNMARKED = R.drawable.board_fieldbackground_bordered
val BG_CHECKED = R.drawable.board_fieldbackground_checked
val BG_MISSED = R.drawable.board_fieldbackground_missed
private val thisBoardEntries = MutableLiveData<List<BoardField>?>()
private val _board = MutableLiveData<List<BoardField>>()
val board: LiveData<List<BoardField>>
get() = _board
private suspend fun getEntries() : List<BoardField>? {
Log.i("GameViewModel", "Running database.getFromParent(boardTitle), function getEntries().")
val entries = database.getFromParent(boardTitle)
return entries
}
init {
viewModelScope.launch {
Log.i("GameViewModel", "Start viewModelScope.launch on init block.")
thisBoardEntries.value = getEntries()
if (thisBoardEntries.value?.isEmpty()!!) {
Log.i(
"GameViewModel",
"allEntries.value is EMPTY, seemingly: ${thisBoardEntries.value}, should be empty"
)
} else {
Log.i(
"GameViewModel",
"allEntries.value is NOT empty, seemingly: ${thisBoardEntries.value}, should be size 25"
)
_board.value = thisBoardEntries.value
}
}
}
fun markFieldMissed(index: Int, view: TextView) {
Log.i("GameViewModel", "My Textview looks like this: $view")
_board.value!![index].marking = BG_MISSED
view.setBackgroundResource(BG_MISSED)
Log.i("GameViewModel", "Set background to $BG_MISSED")
val color = getColor(getApplication(), R.color.white_text_color)
view.setTextColor(color)
viewModelScope.launch {
val markedField = getEntryAtIndex(boardTitle, convertIndexToLocation(index))
Log.i("GameViewModel", "I think markedField is $markedField")
if (markedField != null) {
markedField.marking = BG_MISSED
update(markedField)
Log.i("GameViewModel", "Updated field with $BG_MISSED marking on DB: $markedField")
}
}
}
fun markFieldChecked(index: Int, view: TextView) {
_board.value!![index].marking = BG_CHECKED
view.setBackgroundResource(BG_CHECKED)
Log.i("GameViewModel", "Set background to $BG_CHECKED")
val color = getColor(getApplication(), R.color.white_text_color)
view.setTextColor(color)
viewModelScope.launch {
val markedField = getEntryAtIndex(boardTitle, convertIndexToLocation(index))
Log.i("GameViewModel", "I think markedField is $markedField")
if (markedField != null) {
markedField.marking = BG_CHECKED
update(markedField)
Log.i("GameViewModel", "Updated field with $BG_CHECKED marking on DB: $markedField")
}
}
}
fun debugPrintEntries() {
Log.i("GameViewModel", "DebugPrintEntries function: ${_board.value}")
}
(2020-11-05) Edit 1: Part of the issue was indeed a resource not being read as such. I made the following additions/changes in my layout XML, which gets me a bit further:
<data>
<import type="androidx.core.content.ContextCompat"/>
(...)
</data>
<TextView
(...)
android:background="#{ContextCompat.getDrawable(context, gameViewModel.BG_CHECKED)}"
(...)
With a hardcoded resource for BG_CHECKED as my background image, everything loads and displays nicely. The problem is once again that the background is not read from board[4].marking (which contains BG_CHECKED as its value), although the text has no problem being read from board[4].text
The following replacement in the layout XML does not work, causing an exception: Caused by: android.content.res.Resources$NotFoundException: Resource ID #0x0 with the line
android:background="#{ContextCompat.getDrawable(context, gameViewModel.board[4].marking)}"
I haven't used data binding, but I think it might be because you're just providing an Int as the background, which happens to represent a resource ID - but the data binding doesn't know that, so it doesn't know it needs to resolve it to a drawable value in resources? When you set it manually you're explicitly telling it to do that by using the #drawable syntax
Here's a blog where someone runs into something similar (well that situation anyway, but with colours) - their second solution is to add a ContextCompat import to the data block, and then use that to do aContextCompat.getColor lookup in the data binding expression. Maybe you could do something similar to get the drawable you need

Is DrawableCompat.setTint() lazy?

I am designing an app that has 3 button in main activity, and several buttons in a fragment. I want to change the color of a button in the fragment, depending on which button of main activity is toggled.
color1.setOnClickListener {
brush_chosen = 1
color1.setBackgroundColor(R.color.black)
color2.setBackgroundColor(0x00000000)
color3.setBackgroundColor(0x00000000)
if (frag_num == 8 ){
frag_8p.set_frag_value(frag_num,brush_chosen)
}
}
The function set_frag_value is :
fun set_frag_value(frag_num:Int,brush:Int) : Int
{
brush_chosen=brush
return brush
}
This change the value of brush_chosen. Then I made a function :
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
ib0.setOnClickListener { view ->
Log.d("brush_color","Brush of 0 : "+brush_chosen)
if (brush_chosen==1)
{
Log.d("brush_color","Brush Confirm : "+brush_chosen)
DrawableCompat.setTint(ib0.drawable, ContextCompat.getColor(requireContext(),R.color.rndcolor1))
}
else if (brush_chosen==2)
{
Log.d("brush_color","Brush Confirm : "+brush_chosen)
DrawableCompat.setTint(ib0.drawable, ContextCompat.getColor(requireContext(),R.color.purple_500))
}
else if (brush_chosen==3)
{
Log.d("brush_color","Brush Confirm : "+brush_chosen)
DrawableCompat.setTint(ib0.drawable, ContextCompat.getColor(requireContext(),R.color.teal_200))
}
Log.d("brush_color","End of onclicklistener ")
}
}
I checked the log and theoretically this code should work correctly. However, I found that the button color did not change properly, even I checked my app prints all log correctly. For example, when I clicked button color1 in main activity, variable brush_chosen becomes 1 and the first button in fragment I clicked changes its color. But the second button I clicked does not change its color.
Is there any problem on my code using DrawableCompat ??
Android does some Drawable state caching under the hood. You might need to call mutate() on the Drawable you want to tint and then set the new Drawable in order for the tint to show up properly.

Espresso set cursor for edittext

I am trying to test an EditText that already contains some text using Espresso. The problem is that when I use typeText(), the cursor is placed at an arbitrary position within the text. I tried performing click() before using typeTextIntoFocusedView but the cursor is sometimes placed at the beginning of the EditText. I want to know is it possible to set the cursor at the end of the EditText before typing text into it?
A better way would be to use Espresso the way it's meant to be used: with actions on view matchers.
Example in Kotlin:
class SetEditTextSelectionAction(private val selection: Int) : ViewAction {
override fun getConstraints(): Matcher<View> {
return allOf(isDisplayed(), isAssignableFrom(EditText::class.java))
}
override fun getDescription(): String {
return "set selection to $selection"
}
override fun perform(uiController: UiController, view: View) {
(view as EditText).setSelection(selection)
}
}
Example usage:
onView(withId(R.id.my_text_view).perform(SetEditTextSelectionAction(selection))
An extra advantage over manually doing findViewById() is that you can combine this with matchers like withSubString("my text") if you don't have the ID of the view.
By the way: to change this into setting selection at the end of text you can simply remove the selection: Int constructor argument and change setSelection(selection) to setSelection(view.text.lastIndex).
The only way I have found to do this is to get a reference to the EditText itself and use EditText#setSelection(). For example, to move the cursor to the end of the current text:
val activity = activityRule.activity
val tv = activity.findViewById<EditText>(R.id.edittext)
activity.runOnUiThread { tv.setSelection(tv.text.length) }
I've had success by inserting the KeyCodes for "Home" and "End". These work just like on your desktop keyboard, by moving the cursor to either the beginning or end of the EditText. For example:
onView(withId(R.id.myView))
.perform(pressKey(KeyEvent.KEYCODE_MOVE_HOME))
To move to the end, you can use KeyEvent.KEYCODE_MOVE_END, and you can move left or right using KeyEvent.KEYCODE_DPAD_LEFT and KeyEvent.KEYCODE_DPAD_RIGHT.
I wanted to post my answer since I just had this problem and none of the other answers solved my problem.
I used a GeneralClickAction to click on the right side of the edit text which put the cursor at the end of the EditText where I wanted it. After that I used the TypeTextAction and disabled the tapToFocus behavior by passing in false to the constructor:
onView(withId(R.id.edit_text))
.perform(
new GeneralClickAction(Tap.SINGLE, GeneralLocation.CENTER_RIGHT, Press.FINGER, 0, 0, null),
new TypeTextAction(text, false)
);

How to set selected item in MvxSpinner

I have an MvxSpinner that is bound to a List<PhotoCategory> thus:
<Mvx.MvxSpinner
style="#style/Spinners"
android:id="#+id/photoCategorySpinner"
android:prompt="#string/photoCategory_prompt"
local:MvxBind="ItemsSource PhotoCategories; SelectedItem SelectedPhotoCategory; Visibility ShowPhotoFields, Converter=Visibility"
local:MvxDropDownItemTemplate="#layout/spinner_photocategories"
local:MvxItemTemplate="#layout/item_photocategory" />
The SelectedPhotoCategory that the SelectedItem is bound to is also a PhotoCategory. When this screen is in "update mode", the ViewModel sets the SelectedPhotoCategory to the PhotoCategory whose PhotoCategoryId matches the one in the SQLite database. However, when the spinner is displayed, the default value (which I add to the PhotoCategories property, PhotoCategory = 0, CategoryName="[Choose a Category]") is shown. The only fix I've found is this (which works ok) code added to the View:
protected override void OnCreate(Bundle bundle) {
base.OnCreate(bundle);
SetContentView(Resource.Layout.PhotoView);
//If we're in Update mode, select the relevant photo category in the spinner:
PhotoViewModel photoViewModel = (PhotoViewModel)ViewModel;
if (photoViewModel.ScreenMode == Constants.ScreenMode.Update) {
MvxSpinner photoCategorySpinner = FindViewById<MvxSpinner>(Resource.Id.photoCategorySpinner);
int itemPosition = 0;
int selectedPhotoCategoryId = photoViewModel.SelectedPhotoCategory.PhotoCategoryId;
foreach (PhotoCategory photoCategory in photoViewModel.PhotoCategories) {
if (photoCategory.PhotoCategoryId == selectedPhotoCategoryId) {
photoCategorySpinner.SetSelection(itemPosition);
}
itemPosition++;
}
}
I've also tried using the GetPosition method of the MvxSpinner.Adapter but this always returns -1 for PhotoCategoryId, CategoryName or SelectedPhotoCategory as the parameter value.
What am I missing??
The binding
SelectedItem SelectedPhotoCategory
should set this for you - and should use Equals to find the correct item to select in the spinner.
This certainly seems to work in the very latest code when testing using the SpinnerViewModel in https://github.com/slodge/MvvmCross-Tutorials/tree/master/ApiExamples
I know there was a bug reported recently on the use of == versus Equals in one of the bindings - but I don't think this effects the spinner (see https://github.com/slodge/MvvmCross/issues/309).

Categories

Resources