I'm creating my Android application and faced this peculiar issue.
None of my teammates have experienced this before and I'm stuck with this for days now.
So what happens is that after going to Setting Fragment and coming back, ALL images (both ImageView and Facebook SimpleDraweeView) become white.
If you see pictures below, you may see how all images change to white.
This always occur if I tap the Setting tab (the last one on the right) and coming back to any tab.
This line of my Setting Fragment is what makes the problem.
childFragmentManager.beginTransaction().replace(R.id.settings_container, MainSettingsFragment()).commit()
If I comment this line out, then everything is fine. The problem is, the line below doesn't cause any problem. While the above line is for the fragment, the below line is for activity.
supportFragmentManager.beginTransaction().replace(R.id.msettings_container, MainSettingsFragment()).commit()
The below is the code block of fragment lifecycle.
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
profile_profileimage.setOnClickListener {
val imageList = arrayListOf(MainActivity.myProfile)
val overlay = ImageOverlayView(requireContext())
ImageViewer.Builder(requireContext(), imageList)
.hideStatusBar(false)
.allowZooming(true)
.allowSwipeToDismiss(true)
.setOverlayView(overlay)
.setImageChangeListener { position ->
overlay.setImageUrl(MainActivity.myProfile)
}
.show()
}
}
override fun onResume() {
super.onResume()
refreshMemorials()
loadProfile()
}
private fun loadProfile() {
activity?.runOnUiThread {
...
profile_profileimage.setImageURI(MainActivity.myProfile)
...
}
}
This is the XML code for profile_profileimage
<com.facebook.drawee.view.SimpleDraweeView
android:layout_width="120dp"
android:layout_height="120dp"
android:id="#+id/profile_profileimage"
app:actualImageScaleType="fitXY"
app:roundAsCircle="true"
app:roundingBorderWidth="1dp"
app:roundingBorderColor="#android:color/white" />
But, I want to have Settings as one of fragment.
I can't see what is going wrong here. Is there any way to solve this?
Related
So I'd like to make a function that changes the tint of the view that is calling the function.
I'm pretty sure I got the tint part down I'm just not quite sure how or where I need to define this function so that I can either select it as the onClick method in the built-in properties menu or I can reference it in the xml file(preferably the former).
Right now I have the function in the MainActivity.kt file inside the class and I selected the function on all the different views in the properties menu but when I run the app and actually click on of these views I get a crash saying "Could not find method in parent or ancestor Context for android:onClick attribute"
I would really appreciate some help with this, thanks in advance!
You can set the same on click listener to multiple views.
val tintChanger = View.OnClickListener { view ->
println("View with id=${view.id} clicked")
changeTintOf(view as ImageView)
}
imageViewOne.setOnClickListener(tintChanger)
imageViewTwo.setOnClickListener(tintChanger)
imageViewThree.setOnClickListener(tintChanger)
How to set on click listener to views without knowing their ids?
val imageContainerLayout = findViewById<LinearLayout>(R.id.imageContainer)
// val imageContainerLayout = binding.imageContainer
imageContainerLayout.children.forEach {
it.setOnClickListener(tintChanger)
}
// xml
<LinearLayout
android:id="#+id/imageContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<ImageView ... /> // without android:id set
<ImageView ... /> // without android:id set
<ImageView ... /> // without android:id set
</LinearLayout>
Not preferred way nowadays but if you want to set a click listener on your view by xml, your activity should contain a public method changeTintOnClick with an argument view: View.
// MainActivity.kt
fun changeTintOnClick(view: View) {
println("View click listener set by XML")
println("View clickView with id=${view.id} clicked")
changeTintOf(view as ImageView)
}
private fun changeTintOf(view: ImageView) {
// your implementation for tint
}
<ImageView ...
android:onClick="changeTintOnClick"
/>
One way you can pull this of is placing an OnClickListener extension on your main activity class and set all of the views with an id and tag to reference later.
class MainActivity : AppCompatActivity(), View.OnClickListener{ /*Extend class to conformalso to View.OnClickListsner*/
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
//Let's say you have two or more views to use.
//You want to include the same listener to all opf the same buttons you want to use
findViewById<Button>(R.id.myelementone).setOnClickListener(this)
findViewById<Button>(R.id.myelementTwo).setOnClickListener(this)
override fun onClick(v: View) {
switch(v.tag){
case "myelementonetag":
//Do something
break;
case "myelementtwotag"
//Do something else
break;
default:
//If no tags match the clicked item
break;
}
}
}
The only thing you really need to do in XML is set the id of the element and the tag of the elements like this (using onClick in XML is no longer best practice and advised that it should not be used anymore. Dont forget to keep they styling for you own button!):
<Button
android:id="#+id/myelementone"
tag="myelementonetag"/>
<Button
android:id="#+id/myelementone"
tag="myelementonetag"/>
If you need a little more on how this works, here is another StackOverflow question that was answered with all best ways to implement click functions: Android - How to achieve setOnClickListener in Kotlin?
I'm using androidx.viewpager2.widget.ViewPager2.
When user swipes to last page, I want the application to run some code.
I want ViewPager2 to run some code when user swipes to last page (locking the swipe and some other code).
How can I make this happen? What method on ViewPager2 should I use?
Try this:
binding.viewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
if (position == x) { //you last page index
//do what ever you want
}
}
})
Helpful reading: https://proandroiddev.com/look-deep-into-viewpager2-13eb8e06e419
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
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.
I got a RecyclerView with multiple EditText-fields. When I try to edit one of the EditText-fields and click enter on the virtual keyboard, the focus shifts down to the next EditText-field, something I don't want to happen. I want to submit the changes I made in the first EditText-field and then close the keyboard. I managed to turn off this focus-shifting by adding the following to my .xml file:
android:focusable="true"
android:focusableInTouchMode="true"
But the problem still persists, now the changes just never get submitted as my listener never gets called. If I remove all items except from one in my RecyclerView everything works like I want. How can I make that happen with more items in myRecyclerView too?
My bind function inside my UserCardItem.kt file;
override fun bind(viewHolder: ViewHolder, position: Int) {
...
viewHolder.itemView.creditcard_nickname.setOnEditorActionListener{ _, actionId, _ ->
if(actionId == EditorInfo.IME_ACTION_DONE){
saveNickname(viewHolder)
true
} else {
false
}
}
private fun saveNickname(viewHolder : ViewHolder){
val nickname = viewHolder.itemView.creditcard_nickname.text.toString()
userCreditcard.nickname = nickname
UserCardStore().updateNickname(userCreditcard)
}
Add android:imeOptions="actionDone" to your EditText in your layout XML.