I have an Android spinner that I would like to disable when there's less than one item in my list. What I mean by that is I would like the title to show up, but I don't want a list to drop down when I click it.
I've tried isClickable = false, and isEnabled = false like in many of the posts out there, but I still can't get it to work.
I successfully get ride of the spinner dropdown icon, but I can still click the text and get the list.
class CustomSpinnerAdapter(private val ctx: Context, val locations: List<Location>) : ArrayAdapter<Location>(ctx, R.layout.list_item_spinner_view) {
override fun isEmpty() = locations.isEmpty()
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val context = parent.context
val inflater = LayoutInflater.from(context)
var view = convertView
if (view == null)
view = inflater.inflate(R.layout.list_item_spinner_view, parent, false)
view!!.location_spinner_name.text = locations[position].name
if (count < 2) {
view.location_spinner.visibility = View.GONE
view.better_name.isEnabled = false
view.better_name.isClickable = false
}
view.location_spinner_name.typeface = Typeface.createFromAsset(ctx.assets, ctx.getString(R.string.font_bold))
return view
}
Here is the view for my title
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:id="#+id/better_name">
<TextView
android:id="#+id/location_spinner_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
style="#style/TextAppearance.Widget.AppCompat.Toolbar.Title"/>
<com.ge.cbyge.view.TintableImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_arrow_drop_down_black"
android:tint="#color/medium_gray"
android:paddingTop="#dimen/activity_home_spinner_dropdown_padding_top"/>
</LinearLayout>
Hopefully this will be helpful. It is not a re-write of your app, but maybe an idea that you can integrate:
You could create your own Spinner class like this:
class MySpinner extends AppCompatSpinner {
public MySpinner(Context context) {
super(context);
}
public MySpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean performClick() {
if ( getCount() == 0 ) {
return true; // we do nothing because adapter contents = 0
}
else {
return super.performClick(); // we proceed as normal
}
}
}
Then alter your XML references from "Spinner" to "[yourPackageName].MySpinner"
Related
i am calling data from API to display it to use but in my case i want to display the latest 4 images of the API, i don't want to call all of them
so what is the correct way to do this?
Here is my code:
Interface:
interface facebookInterface {
#GET(" ")
fun getServices(): Call<List<facebookData>>
companion object Factory {
fun create(): facebookInterface {
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("API_URL")
.build()
return retrofit.create(facebookInterface::class.java)
}
}
}
Adapter:
class facebookAdapter(var countryList: List<facebookData>, var activity: MainActivity): RecyclerView.Adapter<facebookAdapter.ViewHolder>(){
lateinit var context: Context
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): facebookAdapter.ViewHolder {
context = parent.context!!
val view = LayoutInflater.from(context).inflate(R.layout.facebook_list_item, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return countryList.size
}
override fun onBindViewHolder(holder: facebookAdapter.ViewHolder, position: Int) {
holder.name.text = countryList[position].source
holder.capital.text = countryList[position].imageLink
Picasso.get()
.load(countryList[position].imageUrl)
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
.networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE)
.into(holder.thumbnailUrl)
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val name: TextView = itemView.findViewById(R.id.country_name)
val capital: TextView = itemView.findViewById(R.id.country_capital)
val thumbnailUrl: ImageView = itemView.findViewById(R.id.country_flag)
}
}
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
android:id="#+id/frame_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="#+id/list_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="4dp"
app:contentPadding="8dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/list_constraint"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/country_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="monospace"
android:text="TextView"
android:textSize="24sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/country_capital"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:fontFamily="monospace"
android:text="TextView"
android:textSize="18sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/country_name" />
<ImageView
android:id="#+id/country_flag"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>
Activity:
val recyclerView: RecyclerView = findViewById(R.id.list_recycler4)
recyclerView.layoutManager = LinearLayoutManager(this)
val facebookInterface = facebookInterface.Factory.create()
val facebok: Call<List<facebookData>> = facebookInterface.getServices()
facebok.enqueue(object : Callback<List<facebookData>> {
override fun onFailure(call: Call<List<facebookData>>, t: Throwable) {
Log.d("xxxx", t.toString())
Toast.makeText(this#MainActivity, t.message, Toast.LENGTH_SHORT).show()
}
override fun onResponse(
call: Call<List<facebookData>>,
response: Response<List<facebookData>>
) {
Log.d("xxxx", response.body().toString())
val list = response.body()!!
recyclerView.adapter = facebookAdapter(list, this#MainActivity)
}
})
data class:
data class facebookData (
val imageUrl : String,
val imageLink: String,
val source: String
)
i am getting the data and i can see it on the device but is there a way to display only the latest 4 images from API?
and how to link the url of each image so the user can go to the url of each image
"English is not my mother tongue, i will explain more if you didn't understand me"
As Teo said, you are better of applying a filter directly to your API call, but if that is not possible, you can trim off the list you receive.
There are mainly two methods I can think of, that can get you what you want, first is to make a sublist and then assign the adapter as follows.
val trimmedList = if(list.size > 4){
list.subList(fromIndex = 0, toIndex = 3)
} else {
list
}
recyclerView.adapter = facebookAdapter(trimmedList , this#MainActivity)
Second way to override the itemCount method inside the Recycler View Adapter, same logic shall apply there. It will be as follows.
override fun getItemCount(): Int {
return if(countryList.size > 4) {
4
} else {
countryList.size
}
}
For second part of your answer, I am adding the following code for your reference, but I still believe it is better for you to understand what is happening.
To open the url in browser, firstly you need a callback from your Adapter to your MainActivity.
Your adapter will be modified as the following.
class facebookAdapter(var countryList: List<facebookData>,
private val facebookAdapterCallback: FacebookAdapterCallback)
: RecyclerView.Adapter<facebookAdapter.ViewHolder>(){
lateinit var context: Context
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): facebookAdapter.ViewHolder {
context = parent.context!!
val view = LayoutInflater.from(context).inflate(R.layout.facebook_list_item, parent, false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return countryList.size
}
override fun onBindViewHolder(holder: facebookAdapter.ViewHolder, position: Int) {
holder.name.text = countryList[position].source
holder.capital.text = countryList[position].imageLink
holder.thumbnailUrl.setOnClickListener {
facebookAdapterCallback.onFacebookAdapterItemClick(countryList[position])
}
Picasso.get()
.load(countryList[position].imageUrl)
.memoryPolicy(MemoryPolicy.NO_CACHE, MemoryPolicy.NO_STORE)
.networkPolicy(NetworkPolicy.NO_CACHE, NetworkPolicy.NO_STORE)
.into(holder.thumbnailUrl)
}
class ViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
val name: TextView = itemView.findViewById(R.id.country_name)
val capital: TextView = itemView.findViewById(R.id.country_capital)
val thumbnailUrl: ImageView = itemView.findViewById(R.id.country_flag)
}
interface FacebookAdapterCallback{
fun onFacebookAdapterItemClick(data: facebookData)
}
}
Next we will have to modify your activity as follows to implement the above interface inside it
class MainActivity : AppCompatActivity(), facebookAdapter.FacebookAdapterCallback {
override fun onFacebookAdapterItemClick(data: facebookData) {
openLinkInBrowser(this#MainActivity, data.imageLink)
}
fun openLinkInBrowser(context: Context, url: String) {
try {
val url = if (Uri.parse(url).scheme == null || !url.startsWith("https://") && !url.startsWith("http://")) {
"https://$url"
} else url
val webPage: Uri = Uri.parse(url)
val intent = Intent(Intent.ACTION_VIEW, webPage)
if (intent.resolveActivity(context.packageManager) != null) {
context.startActivity(intent)
} else {
Toast.makeText(context, "No App Available for this action", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
I have also added a helper method to open the link in browser, it uses the default Android Intents and checks for apps available in which this action can be performed.
We also check if the URL has http or https appended else we add it.
While I have provided you the code, I would still recommend you to understand what is happening before doing it.
To understand it better read this :
First game :
Skate QuestionMark
Archery QuestionMark
Swim QuestionMark
---------------------------
Water Bow Wheel
If user drags Water to Skate or Archery QuestionMark it will animate to the list (because it is not correct)
If user drags twice incorrect (it will mark the one that is correct from the answer list)
If user still fail in the third try it will drag to the incorrect one and then it will highlight or just change the color doesn't matter to red.
If user drags to the correct one it will highlight green and replace QuestionMark with the correct one (I do not want to be draggable anymore that image)
------
Game 2 (is more or less the same...)
There's no QuestionMark column, there's only :
Skate
Swim
Archery
--------------
(Lot of answers)
Now the way to play is the same (about the fails and etc) the thing is now when I drag any answer to the correct one it won't replace the correct one, it will just disappear and if it fails instead of highlighting all the corrects one it will highlight the correct answer (for instance; if I drag wheel to Swim once, it doesn't happen anything just animate to the place where it was, if I do it twice it will highlight the Skate one, and if it fails at third one it just drag wherever he did and highlight with red)
I'm planning to build an app that does a simple check, I'm calling an endpoint and I'll get some params, and then I'll know how many ImageView are going to be displayed in the screen.
This is like a puzzle, and it would look like this :
So I have different options, which contains only one correct answer, I'm planing the way to achieve this, could be able to drag "Bow" to the questionmark infront of "skateboarding" and then says that is not correct, then drag it to the "archery" one and replace the questionmark for the ImageView from the bottom that contains the word "Arrow".
Layout should contain one column for Question (this should be the sports) then another one in front of the Question one and should be the Answer one, then below them should contain the Options one.
Was it clear? Otherwise let me know and I'll try to explain it a little bit with more details.
EDIT
What I thought is having like a class that contains a list of Answers or just create like :
RightList : (id:1,id:2,id:3)
LeftList : (id:1, id:2, id:3)
DownList : (Bow = id:2),(Skate = id:1), (Ball = id:3)
Then doing the drag and drop thing when the DragEvent.ACTION_DROP or DragEvent.ACTION_DRAG_ENDEDI do not know which one, check (Pseudocode below)
if(imageDragged.id==location.id) then replace the question mark image for imageDragged
else animate the image to the place where it comes
I do not know if creating a class that implements onDragListener() or something like that, I'd like to have it generic so I can use it on different games like for instance :
SKATE(id:1) ARCHERY(id:2) FOOTBALL(id:3)
Answers : TABLE(C.A. id:1) BOW(C.A. id:2) GRASS(C.A. id:3) GOAL(C.A. id:3) BALL(C.A. id:3) ARROW(C.A. id:2) AXES(C.A. id:1) WHEELS(C.A. id:1)
So if I drag and drop for instance BOW to FOOTBALL then it should display that is bad, otherwise say that it's good.
EXAMPLE 1/3
Just for reference and summarize everything. Here is one 100 lines code, within single Activity and imports, representing all this behavior even with simple animation.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bind()
}
private fun bind() {
addQuestions()
addAnswers()
}
#SuppressLint("InflateParams")
private fun addQuestions() {
val inflater = getSystemService(
Context.LAYOUT_INFLATER_SERVICE
) as LayoutInflater
for (i in 1..8) {
val view = inflater.inflate(R.layout.item_question, null)
view.setOnDragListener(DragListener())
questionContainer.addView(view)
}
}
#SuppressLint("InflateParams")
private fun addAnswers() {
val inflater = getSystemService(
Context.LAYOUT_INFLATER_SERVICE
) as LayoutInflater
for (i in 1..8) {
val view = inflater.inflate(R.layout.item_answer, null)
view.setOnTouchListener(DragItemTouchListener())
answerContainer.addView(view)
}
}
private inner class DragItemTouchListener : OnTouchListener {
override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
return if (motionEvent.action == MotionEvent.ACTION_DOWN) {
dragMultiple(view)
true
} else {
false
}
}
private fun dragMultiple(view : View) {
val data = ClipData.newPlainText("", "")
val shadowBuilder = DragShadowBuilder(
view
)
val parent = view.parent as ViewGroup
view.startDragAndDrop(data, shadowBuilder, view, 0)
parent.removeView(view)
}
}
private inner class DragListener : OnDragListener {
override fun onDrag(v: View, event: DragEvent): Boolean {
when (event.action) {
DragEvent.ACTION_DRAG_STARTED -> {
}
DragEvent.ACTION_DRAG_ENTERED -> {
}
DragEvent.ACTION_DRAG_EXITED -> {
}
DragEvent.ACTION_DROP -> {
animateDropEffect(v as ViewGroup, event.localState as View)
}
DragEvent.ACTION_DRAG_ENDED -> {
}
else -> {
}
}
return true
}
private fun animateDropEffect(into: ViewGroup, view: View) {
into.addView(view)
val params = (view.layoutParams as FrameLayout.LayoutParams)
.apply {
gravity = Gravity.END
}
view.layoutParams = params
}
}
}
All Xmls used. Below xml for all examples below.
/* activity_main.xml */
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="#+id/mainContainer"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="500dp"
android:animateLayoutChanges="true">
<LinearLayout
android:id="#+id/questionContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
<LinearLayout
android:id="#+id/answerContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="horizontal">
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
</FrameLayout>
/* item_question.xml */
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:padding="5dp">
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="start"
android:background="#android:color/holo_blue_bright">
</View>
<View
android:id="#+id/questionView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="end"
android:background="#android:color/holo_orange_light">
</View>
</FrameLayout>
/* item_answer.xml */
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5dp"
android:tag="Test">
<LinearLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:background="#android:color/darker_gray">
</LinearLayout>
</FrameLayout>
EXAMPLE 2/3
It's not a problem to make dragging for few elements following with a the same approach. Here is a little crappy, but simple example.
Modified code for second example. Xml stay the same.
class MainActivity : AppCompatActivity() {
var activeOneDrag : Boolean = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bind()
}
private fun bind() {
addQuestions()
addAnswers()
}
fun getRandomColor(): Int {
return Color.argb(255, Random.nextInt(255),
Random.nextInt(255), Random.nextInt(255))
}
#SuppressLint("InflateParams")
private fun addQuestions() {
val inflater = getSystemService(
Context.LAYOUT_INFLATER_SERVICE
) as LayoutInflater
for (i in 1..8) {
val view = inflater.inflate(R.layout.item_question, null)
view.setOnDragListener(DragListener())
questionContainer.addView(view)
}
}
#SuppressLint("InflateParams")
private fun addAnswers() {
val inflater = getSystemService(
Context.LAYOUT_INFLATER_SERVICE
) as LayoutInflater
for (i in 1..8) {
val view = inflater.inflate(R.layout.item_answer, null)
(view as ViewGroup).getChildAt(0).setBackgroundColor(getRandomColor())
view.setOnTouchListener(DragItemTouchListener())
answerContainer.addView(view)
}
}
private inner class DragItemTouchListener : OnTouchListener {
override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
return if (motionEvent.action == MotionEvent.ACTION_DOWN) {
dragMultiple(view)
true
} else {
false
}
}
private fun dragMultiple(view : View) {
val parent = view.parent as ViewGroup
parent.removeView(view)
/**
* Some other logic with selective multiple View.
* Just getting neighbor in our case
*/
var anotherView : View? = null
if (!activeOneDrag) {
anotherView = parent.getChildAt(
parent.indexOfChild(view) + 1)
parent.removeView(anotherView)
}
activeOneDrag = !activeOneDrag
/**
* As you can see, there is postDelay here.
* But only for our case with animateLayoutChanges,
* with delays removing View! In your samples, you could remove it
* with listener on your own animation, if any!
*/
parent.postDelayed({
val layout = LinearLayout(this#MainActivity)
val params = FrameLayout.LayoutParams(
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT)
params.gravity = Gravity.BOTTOM
layout.layoutParams = params
layout.orientation = LinearLayout.HORIZONTAL
layout.addView(view)
if (anotherView != null) {
layout.addView(anotherView)
}
layout.visibility = INVISIBLE
mainContainer.addView(layout)
parent.post {
layout.startDragAndDrop(
ClipData.newPlainText("", ""),
DragShadowBuilder(layout), layout, 0)
}
}, 400)
}
}
private inner class DragListener : OnDragListener {
override fun onDrag(v: View, event: DragEvent): Boolean {
when (event.action) {
DragEvent.ACTION_DRAG_STARTED -> {
}
DragEvent.ACTION_DRAG_ENTERED -> {
}
DragEvent.ACTION_DRAG_EXITED -> {
}
DragEvent.ACTION_DROP -> {
val view = event.localState as View
(view.parent as ViewGroup).removeView(view)
view.visibility = VISIBLE
animateDropEffect(v as ViewGroup, event.localState as View)
}
DragEvent.ACTION_DRAG_ENDED -> {
}
else -> {
}
}
return true
}
private fun animateDropEffect(into: ViewGroup, view: View) {
into.addView(view)
val params = (view.layoutParams as FrameLayout.LayoutParams)
.apply {
gravity = Gravity.END
}
view.layoutParams = params
}
}
}
EXAMPLE 3/3
As I see, it's not clear, how to change simple actions with animation or Drag listening area. Here is another simple example of doing all actions
class MainActivity : AppCompatActivity() {
#Volatile
var state : State = State.INACTIVE
enum class State {
ACTIVE, INACTIVE, HANDLED
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bind()
}
private fun bind() {
addQuestions()
addAnswers()
}
private fun getRandomColor(): Int {
return Color.argb(255, Random.nextInt(255),
Random.nextInt(255), Random.nextInt(255))
}
#SuppressLint("InflateParams")
private fun addQuestions() {
val inflater = getSystemService(
Context.LAYOUT_INFLATER_SERVICE
) as LayoutInflater
for (i in 1..8) {
val view = inflater.inflate(R.layout.item_question, null)
view.findViewById<View>(R.id.questionView)
.setOnDragListener(DragListener())
questionContainer.addView(view)
}
}
#SuppressLint("InflateParams")
private fun addAnswers() {
val inflater = getSystemService(
Context.LAYOUT_INFLATER_SERVICE
) as LayoutInflater
for (i in 1..8) {
val view = inflater.inflate(R.layout.item_answer, null)
(view as ViewGroup).getChildAt(0).setBackgroundColor(getRandomColor())
view.setOnTouchListener(DragItemTouchListener())
answerContainer.addView(view)
}
}
private inner class DragItemTouchListener : OnTouchListener {
val ITEM_INDEX_D = "Index-From"
override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
return if (motionEvent.action == MotionEvent.ACTION_DOWN) {
createDrag(view)
true
} else {
false
}
}
private fun createDrag(view : View) {
val parent = view.parent as ViewGroup
view.tag = Pair(ITEM_INDEX_D,
parent.indexOfChild(view))
view.startDragAndDrop(ClipData.newPlainText("", ""),
DragShadowBuilder(view), view, 0)
parent.removeView(view)
parent.setBackgroundColor(Color.WHITE)
}
}
private inner class DragListener : OnDragListener {
override fun onDrag(parent: View, event: DragEvent): Boolean {
val view = event.localState as View
when (event.action) {
DragEvent.ACTION_DRAG_STARTED -> {
state = State.ACTIVE
}
DragEvent.ACTION_DRAG_ENTERED -> {
}
DragEvent.ACTION_DRAG_EXITED -> {
}
DragEvent.ACTION_DROP -> {
state = State.HANDLED
animateDropEffect(parent, view)
return true
}
DragEvent.ACTION_DRAG_ENDED -> {
if (state == State.ACTIVE) {
state = State.INACTIVE
animateMoveBack(view,
(view.tag as Pair<*, *>).second as Int)
}
return true
}
else -> {
}
}
return true
}
private fun animateMoveBack(view: View, index : Int) {
answerContainer.addView(view, index)
}
private fun animateDropEffect(into: View, view: View) {
val parent = (into.parent as ViewGroup)
parent.addView(view)
val params = (view.layoutParams as FrameLayout.LayoutParams)
.apply {
gravity = Gravity.END
}
view.layoutParams = params
checkIsCorrect(parent)
}
private fun checkIsCorrect(parent : ViewGroup) {
val correct = Random.nextBoolean()
val colorFrom = Color.WHITE
val colorTo : Int = if (correct) 0x8000ff00.toInt() else 0x80ff0000.toInt()
ObjectAnimator.ofObject(
parent,
"backgroundColor",
ArgbEvaluator(),
colorFrom,
colorTo
)
.setDuration(1000)
.start()
}
}
}
UPDATE
The last update from the comments sections. I think it's enough, and of course you would need you changes. So just change two "if" statement to align with your requirements and animation.
class MainActivity : AppCompatActivity() {
enum class State {
ACTIVE, INACTIVE, HANDLED
}
var state : State = State.INACTIVE
var failsCount = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
bind()
}
private fun bind() {
addQuestions()
addAnswers()
}
private fun getRandomColor(): Int {
return Color.argb(255, Random.nextInt(255),
Random.nextInt(255), Random.nextInt(255))
}
#SuppressLint("InflateParams")
private fun addQuestions() {
val inflater = getSystemService(
Context.LAYOUT_INFLATER_SERVICE
) as LayoutInflater
for (i in 1..3) {
val view = inflater.inflate(R.layout.item_question, null)
view.findViewById<View>(R.id.questionView)
.setOnDragListener(DragListener())
questionContainer.addView(view)
}
}
#SuppressLint("InflateParams")
private fun addAnswers() {
val inflater = getSystemService(
Context.LAYOUT_INFLATER_SERVICE
) as LayoutInflater
for (i in 1..3) {
val view = inflater.inflate(R.layout.item_answer, null)
(view as ViewGroup).getChildAt(0).setBackgroundColor(getRandomColor())
view.setOnTouchListener(DragItemTouchListener())
answerContainer.addView(view)
}
}
private inner class DragItemTouchListener : OnTouchListener {
val ITEM_INDEX_D = "Index-From"
override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
return if (motionEvent.action == MotionEvent.ACTION_DOWN) {
createDrag(view)
true
} else {
false
}
}
private fun createDrag(view : View) {
val parent = view.parent as ViewGroup
view.tag = Pair(ITEM_INDEX_D,
parent.indexOfChild(view))
view.startDragAndDrop(ClipData.newPlainText("", ""),
DragShadowBuilder(view), view, 0)
parent.removeView(view)
parent.setBackgroundColor(Color.WHITE)
}
}
private inner class DragListener : OnDragListener {
val ANIM_DURATION_LONG = TimeUnit.SECONDS.toMillis(1)
val ANIM_DURATION_SHORT = TimeUnit.MILLISECONDS.toMillis(500)
val GREEN_ALPHA = 0x8000ff00.toInt()
val RED_ALPHA = 0x80ff0000.toInt()
val ANIM_COLOR = "backgroundColor"
override fun onDrag(parent: View, event: DragEvent): Boolean {
val view = event.localState as View
when (event.action) {
DragEvent.ACTION_DRAG_STARTED -> {
state = State.ACTIVE
}
DragEvent.ACTION_DRAG_ENTERED -> {
}
DragEvent.ACTION_DRAG_EXITED -> {
}
DragEvent.ACTION_DROP -> {
state = State.HANDLED
animateDropEffect(parent, view)
return true
}
DragEvent.ACTION_DRAG_ENDED -> {
if (state == State.ACTIVE) {
state = State.INACTIVE
animateMoveBack(view,
(view.tag as Pair<*, *>).second as Int)
}
return true
}
else -> {
}
}
return true
}
private fun animateMoveBack(view: View, index : Int) {
answerContainer.addView(view, index)
}
private fun animateDropEffect(into: View, view: View) {
val parent = (into.parent as ViewGroup)
parent.addView(view)
val params = (view.layoutParams as FrameLayout.LayoutParams)
.apply {
gravity = Gravity.END
}
view.layoutParams = params
checkIsCorrect(parent)
}
private fun checkIsCorrect(parent : ViewGroup) {
val correct = false
if (correct) {
animateColorChange(parent, true)
return
}
if (++failsCount > Companion.MAX_FAIL_COUNT) {
animateColorChange(parent, false)
return
}
animateWrongAttempt(parent)
}
private fun animateWrongAttempt(parent: ViewGroup) {
val questionMark = parent.findViewById<View>(R.id.questionView)
questionMark.setBackgroundColor(Color.RED)
val va = ValueAnimator.ofFloat(1f, 1.1f)
va.interpolator = BounceInterpolator()
va.duration = ANIM_DURATION_SHORT
va.addUpdateListener { animation ->
questionMark.scaleX = animation.animatedValue as Float
questionMark.scaleY = animation.animatedValue as Float
}
va.start()
}
private fun animateColorChange(parent : ViewGroup, right : Boolean) {
val colorFrom = Color.WHITE
ObjectAnimator
.ofObject(parent, ANIM_COLOR,
ArgbEvaluator(), colorFrom,
if (right) GREEN_ALPHA else RED_ALPHA)
.setDuration(ANIM_DURATION_LONG)
.start()
}
}
companion object {
const val MAX_FAIL_COUNT = 2
}
}
And new xml.
/* activity_main.xml */
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="#+id/mainContainer"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<ScrollView
android:layout_width="match_parent"
android:layout_height="500dp"
android:animateLayoutChanges="true">
<LinearLayout
android:id="#+id/questionContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
<LinearLayout
android:id="#+id/answerContainer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:orientation="horizontal">
</LinearLayout>
</HorizontalScrollView>
</LinearLayout>
</FrameLayout>
/* item_question.xml */
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:animateLayoutChanges="true"
android:paddingTop="10dp">
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="start"
android:layout_margin="5dp"
android:background="#android:color/holo_blue_bright">
</View>
<View
android:id="#+id/questionView"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="end"
android:layout_margin="5dp"
android:background="#android:color/holo_orange_light">
</View>
</FrameLayout>
/* item_answer.xml */
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5dp"
android:tag="Test">
<LinearLayout
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="center"
android:background="#android:color/darker_gray">
</LinearLayout>
</FrameLayout>
I think, there are various ways to do it. For me, seems nice, to split the screen into two main sections. a) To have single vertical list with Items and connections to "Question mark", refer to the image below.
Single row in a vertical list
So it's easy to change size to the item list, which you are retrieving from Server, by dynamically adding and removing simple View. Once correct tile would be places on the top of "Question mark" you will change new View on the top of this View. b) To create bottom list with possible "Answers" there will be Horizontal list, with this items. As you mentioned in our question.
Bottom horizontal list
Description
a) I really do't like implementation of such kind of task with Android widgets like RecyclerView. With small list of items, we could bring a lot of customization. For the first list I would use VerticalScrollView with LinearLayout and View (with any your layout).
Note. Your last layout could be ConstraintLayout. Ex. 1. In this case your dragged items would stick on exact places, where you live it. And In case it's stick on specific row with more than half size, you could change view for it (to green or red). Ex 2. Maybe even better way would be animate moving from place where live the tiles, to the nearest "Question mark" with "Property Animation"
b) To create bottom list I would use the same structure with `HorizontalListView". This will help to use "Property Animation" and not just animate dragging, but also move an object with your grading trajectory. Using dynamically added items in the containers with simple views will reflect on changes in bottom, or top lists.
Implementation
1) Initial listeners for each your "Answer" tiles.
// Assign the touch listener to your view which you want to move
findViewById(R.id.myimage1).setOnTouchListener(new MyTouchListener());
// This defines your touch listener
private final class MyTouchListener implements OnTouchListener {
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction() == MotionEvent.ACTION_DOWN) {
ClipData data = ClipData.newPlainText("", "");
DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(
view);
view.startDrag(data, shadowBuilder, view, 0);
view.setVisibility(View.INVISIBLE);
return true;
} else {
return false;
}
}
}
2) Define each of your target Views in the top list.
findViewById(R.id.bottomright).setOnDragListener(new MyDragListener());
class MyDragListener implements OnDragListener {
Drawable enterShape = getResources().getDrawable(
R.drawable.shape_droptarget);
Drawable normalShape = getResources().getDrawable(R.drawable.shape);
#Override
public boolean onDrag(View v, DragEvent event) {
int action = event.getAction();
switch (event.getAction()) {
case DragEvent.ACTION_DRAG_STARTED:
// do nothing
break;
case DragEvent.ACTION_DRAG_ENTERED:
v.setBackgroundDrawable(enterShape);
break;
case DragEvent.ACTION_DRAG_EXITED:
v.setBackgroundDrawable(normalShape);
break;
case DragEvent.ACTION_DROP:
// Dropped, reassign View to ViewGroup
View view = (View) event.getLocalState();
ViewGroup owner = (ViewGroup) view.getParent();
owner.removeView(view);
LinearLayout container = (LinearLayout) v;
container.addView(view);
view.setVisibility(View.VISIBLE);
break;
case DragEvent.ACTION_DRAG_ENDED:
v.setBackgroundDrawable(normalShape);
default:
break;
}
return true;
}
}
3) That is it you need to create dragging. Just to make another setup in the Activity and View changes.
However you could check few other examples of drag and drop implementation. But since you were using simple Views and Scrollable containers, you can align every sample to work with your lists.
I have a RecyclerView that I want to always have 3 columns. For some reason the width when using a GridLayoutManager seems to be a static width and I'm not sure how to get it to match the screen.
On iOS it is easy as I use a UICollectionView and auto layout manages that like so:
How do I get the same result on Android? Here is my content_symptom_tracker.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/bzPrimary"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".SymptomTracker"
tools:showIn="#layout/activity_symptom_tracker">
<com.cryptixltd.peterruppert.brainzaps.GridRecyclerView
android:id="#+id/symptomRecycler"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:foregroundGravity="center"
android:layoutAnimation="#anim/grid_layout_animation_from_bottom"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</android.support.constraint.ConstraintLayout>
Here is how I setup the Recycler in the Activity:
recyclerView = findViewById(R.id.symptomRecycler);
int numberOfColumns = 3;
recyclerView.setLayoutManager(new GridLayoutManager(this, numberOfColumns));
adapter = new SymptomAdapter(this, common_symptoms);
recyclerView.setAdapter(adapter);
But it gives me this result, the same width no matter the device:
I don't want to change the size of the icons, just make the spacing of the width match the parent basically.
Thanks!
EDIT: Here is the GridRecyclerView class, it is a custom class to help with the Grid Animations:
public class GridRecyclerView extends RecyclerView {
public GridRecyclerView(Context context) { super(context); }
public GridRecyclerView(Context context, AttributeSet attrs) { super(context, attrs); }
public GridRecyclerView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); }
#Override
protected void attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params,
int index, int count) {
final LayoutManager layoutManager = getLayoutManager();
if (getAdapter() != null && layoutManager instanceof GridLayoutManager){
GridLayoutAnimationController.AnimationParameters animationParams =
(GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;
if (animationParams == null) {
// If there are no animation parameters, create new once and attach them to
// the LayoutParams.
animationParams = new GridLayoutAnimationController.AnimationParameters();
params.layoutAnimationParameters = animationParams;
}
// Next we are updating the parameters
// Set the number of items in the RecyclerView and the index of this item
animationParams.count = count;
animationParams.index = index;
// Calculate the number of columns and rows in the grid
final int columns = ((GridLayoutManager) layoutManager).getSpanCount();
animationParams.columnsCount = columns;
animationParams.rowsCount = count / columns;
// Calculate the column/row position in the grid
final int invertedIndex = count - 1 - index;
animationParams.column = columns - 1 - (invertedIndex % columns);
animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns;
} else {
// Proceed as normal if using another type of LayoutManager
super.attachLayoutAnimationParameters(child, params, index, count);
}
}
}
try to set the GridRecyclerView to match constraints.
in your case just make android:layout_width=0dp.
I have a Coordinator Layout like so:
<android.support.design.widget.CoordinatorLayout
android:id="#+id/coordinator_subreddit_selection"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.github.clans.fab.FloatingActionMenu
android:id="#+id/addFabMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"/>
</android.support.design.widget.CoordinatorLayout>
And in my code, I have a snackbar like so:
Snackbar.make(getView().findViewById(R.id.addFabMenu),
R.string.no_entered_subreddit_name, Snackbar.LENGTH_SHORT).show();
Here is what I get:
And here is what I what:
I've tried copying the code from the example activity exactly but it still does not work like the example activity.
Fixed it by creating a Coordinator behaviour like so:
public class MoveUpwardBehavior extends CoordinatorLayout.Behavior<View> {
private static final boolean SNACKBAR_BEHAVIOR_ENABLED;
#Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
return SNACKBAR_BEHAVIOR_ENABLED && dependency instanceof Snackbar.SnackbarLayout;
}
#Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
float translationY = Math.min(0, dependency.getTranslationY() - dependency.getHeight());
child.setTranslationY(translationY);
return true;
}
static {
SNACKBAR_BEHAVIOR_ENABLED = Build.VERSION.SDK_INT >= 11;
}
}
And extended my view class, applying the behaviour to it
#CoordinatorLayout.DefaultBehavior(MoveUpwardBehavior.class)
public class MoveUpwardsFloatingMenu extends FloatingActionMenu {
public MoveUpwardsFloatingMenu(Context context) {
super(context);
}
public MoveUpwardsFloatingMenu(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MoveUpwardsFloatingMenu(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
Try using the standard FloatingActionButton from the support library
Instead of:
<com.github.clans.fab.FloatingActionMenu
android:id="#+id/addFabMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"/>
Use:
<android.support.design.widget.FloatingActionButton
android:id="#+id/addFabMenu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"/>
CoordinatorLayout.DefaultBehavior annotation was deprecated from API
level 27.1.0.
To add a Behavior to your custom View, you just need to implement CoordinatorLayout.AttachedBehavior interface to return the default behavior or using layout_behavior attribute that is present will override the AttachedBehavior.
Here you can see an example:
class MoveUpwardBehavior : CoordinatorLayout.Behavior<View>() {
override fun layoutDependsOn(
parent: CoordinatorLayout,
child: View,
dependency: View): Boolean = dependency is SnackbarLayout
override fun onDependentViewChanged(
parent: CoordinatorLayout,
child: View,
dependency: View
): Boolean {
child.translationY = min(DEFAULT_CHILD_SIZE, dependency.translationY - dependency.height)
return true
}
override fun onDependentViewRemoved(parent: CoordinatorLayout, child: View, dependency: View) {
super.onDependentViewRemoved(parent, child, dependency)
child.translationY = DEFAULT_CHILD_SIZE
}
companion object {
private const val DEFAULT_CHILD_SIZE = 0f
}
}
class ExampleComponentView #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : ConstraintLayout(context, attrs, defStyleAttr), CoordinatorLayout.AttachedBehavior {
.....
override fun getBehavior(): CoordinatorLayout.Behavior<*> =
MoveUpwardBehavior()
}
So I've got this problem in my custom view. I'm trying to create a custom RelativeLayout with an infinite scrolling animation. To achieve this, I've created a layout backgroundscrollrelativelayout.xml like so:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mainTile"/>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/topTile" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/leftTile" />
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/diagonalTile" />
</RelativeLayout>
The idea is that the ImageViews will translate their position on an animation update callback.
I've created the BackgroundScrollRelativeLayout.java like so:
public class BackgroundScrollRelativeLayout extends RelativeLayout {
final int layoutToUse = R.layout.backgroundscrollrelativelayout;
final int mainTileId = R.id.mainTile;
final int leftTileId = R.id.leftTile;
final int topTileId = R.id.topTile;
final int diagonalTileId = R.id.diagonalTile;
final float valueStart = 0.0f;
final float valueEnd = 1.0f;
final long animationDuration = 50000L;
private Context mContext;
private ValueAnimator scrollAnimator;
private ImageView mainTile;
private ImageView leftTile;
private ImageView topTile;
private ImageView diagonalTile;
public BackgroundScrollRelativeLayout(Context context) {
super(context);
mContext = context;
acquireViewsInLayout();
initializeAnimator();
scrollAnimator.start();
}
public BackgroundScrollRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
acquireViewsInLayout();
initializeAnimator();
scrollAnimator.start();
}
public BackgroundScrollRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
acquireViewsInLayout();
initializeAnimator();
scrollAnimator.start();
}
#Override
public void setBackgroundColor(int color) {
// Not supported
}
#Override
public void setBackgroundResource(int resid) {
// Not supported
}
#Override
public void setBackground(Drawable background) {
mainTile.setBackground(background);
leftTile.setBackground(background);
}
/*
#Override
public void setBackgroundDrawable(Drawable background) {
//super.setBackgroundDrawable(background);
//mainTile.setBackground(background);
//leftTile.setBackground(background);
}*/
// Intent: Inflate the layout associated with this View
private void inflateLayout(){
// TO inflateLayout, we connect the inflater to context, then we call inflate the layout associated
// with this view
//LayoutInflater inflater = LayoutInflater.from(mContext);
//inflater.inflate(layoutToUse, this);
inflate(getContext(), layoutToUse, this);
}
// Intent: Find all Views in Layout
private void findAllViewsById(){
mainTile = (ImageView)this.findViewById(mainTileId);
leftTile = (ImageView)this.findViewById(leftTileId);
topTile = (ImageView)this.findViewById(topTileId);
diagonalTile = (ImageView)this.findViewById(diagonalTileId);
}
// Intent: Concretely acquire all Views in Layout
private void acquireViewsInLayout(){
// TO acquireViewsInLayout, we inflate the layout,
// then we find the view of each known view id and save the view
inflateLayout();
findAllViewsById();
}
// Intent: Initialize animator properties
private void initializeAnimator(){
// TO initializeAnimator, we set how the animator will keep track of animation,
// then we set the animation repeat type, then we set the type of interpolation,
// then we set the animation duration, then we apply animation update listener
scrollAnimator = ValueAnimator.ofFloat(valueStart, valueEnd);
scrollAnimator.setRepeatCount(ValueAnimator.INFINITE);
scrollAnimator.setInterpolator(new LinearInterpolator());
scrollAnimator.setDuration(animationDuration);
addScrollAnimatorUpdateListener();
}
// Intent: Add an update listener to the scroll animator
private void addScrollAnimatorUpdateListener(){
// TO addScrollAnimatorUpdateListener, we add an update listener to scroll animator
scrollAnimator.addUpdateListener( new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
// Do something...
updateScrollAnimation();
}
});
}
private void updateScrollAnimation(){
float progress = (float)scrollAnimator.getAnimatedValue();
float widthOfTile = mainTile.getWidth();
float moveInXAxis = widthOfTile * progress;
mainTile.setTranslationX(moveInXAxis);
leftTile.setTranslationX(moveInXAxis - widthOfTile);
// Ignore the rest for now
topTile.setTranslationY(-1.0f);
diagonalTile.setTranslationX(-1.0f);
diagonalTile.setTranslationY(-1.0f);
}
}
I use my custom view like so in an activity:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.martianstudio.adivinaque.AppSettingsActivity">
<com.martianstudio.adivinaque.BackgroundScrollRelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/main_activity_animation_icon" />
</RelativeLayout>
The goal of this is to allowing the user to specify the background that they want to scroll with android:background. I do not want the parent RelativeLayout to take on this drawable as a background. Instead, I want the ImageViews to set the drawable as the background. I have overridden the setBackground in my custom view so that only the ImageViews set the drawable as the background and not the RelativeLayout (as it would by default).
#Override
public void setBackground(Drawable background) {
mainTile.setBackground(background);
leftTile.setBackground(background);
}
However, I get this error:
java.lang.NullPointerException at com.martianstudio.adivinaque.BackgroundScrollRelativeLayout.setBackground
This says that mainTile in setBackground has not been set (it is null). In my findAllViewsById() I explicitly write this.mainTile = (ImageView)this.findViewById(mainTileId);, where mainTileId = R.id.mainTile, and I inflate the layout in inflateLayout(). Both of these methods are called in the constructors of the class. It seems to me that for some reason, it cannot find the ImageView with the mainTile Id when I override the setBackground method like I do. If I don't override the setBackground method, I do not get a NullPointerException, however, it defaults to the default setBackground method, which is what I do not want. Am I missing somthing?
Any help will be appreciated :). Thank you!