Мне нужно onDraw
предметов RecyclerView
.Используя подход, «обнаруженный» в этой SO-ссылке , я получил ... ну - частично там.
Обратите внимание, что в конечном итоге я хочу onDraw
"поверх" пользовательского представления.Имеется в виду вызов super
, чтобы позволить рисованию по умолчанию произойти, затем paint
по неиспользуемым областям (вида) canvas
.
Перед тем как начать движение по этому «настраиваемому виду, чтобы разрешить onDraw
» дорогу,было то, что вы видите на слева ниже:
После этого у меня были все "невидимые" виды ( середина изображение выше).Я говорю «невидимка», потому что они все еще были там, чтобы их нажимали.Чтобы помочь мне визуализировать вещи (и немного onDraw
подтверждение концепции), я переопределил onDraw
в пользовательском представлении PuzzleView
, просто вызвав canvas.drawRect
, чтобы покрыть весь холст зеленым цветом, и теперь вижу право изображение выше.
Я не уверен, что я делаю здесь неправильно.
ТАКЖЕ: Если вам приходит в голову, что - ну, почему бы мне просто не onDraw
все это ... это не практично по ряду причин.
Итак, вотмой PuzzleAdapter, как он есть :
class PuzzleAdapter(private val puzzles: List<Puzzle>) : RecyclerView.Adapter<PuzzleAdapter.PuzzleHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PuzzleAdapter.PuzzleHolder {
//val v = LayoutInflater.from(parent.context).inflate(R.layout.item_puzzle, parent, false)
//not inflating, creating PuzzleView (which is inflating itself)
val v = PuzzleView(parent.context)
v.layoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
return PuzzleHolder(v)
}
override fun getItemCount() = puzzles.size
//unchanged between these two versions
override fun onBindViewHolder(h: PuzzleAdapter.PuzzleHolder, pos: Int) {
val p = puzzles[pos]
h.view.puzzleItem_text_ndx.text = "# " + p.descr()
h.view.puzzleItem_text_details.text = "Ndx: ${p.puzzleNdx}"
h.view.setOnClickListener {
Log.d("##", "PuzzleHolder.onClick (bunch#${p.parentBunch.bunchID}; puzzle#${p.puzzleNdx})")
val action = BunchFragmentDirections.navBunchToPuzzle(PuzzleParcel(p.parentBunch.bunchID, p.puzzleNdx))
it.findNavController().navigate(action)
}
}
inner class PuzzleHolder(v: View) : RecyclerView.ViewHolder(v) {
val view: PuzzleView
init {
view = v as PuzzleView
}
}
}
PuzzleAdapter до :
class PuzzleAdapter(private val puzzles: List<Puzzle>) : RecyclerView.Adapter<PuzzleAdapter.PuzzleHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PuzzleAdapter.PuzzleHolder {
val v = LayoutInflater.from(parent.context).inflate(R.layout.item_puzzle, parent, false)
return PuzzleHolder(v)
}
override fun getItemCount() = puzzles.size
//unchanged between these two versions
override fun onBindViewHolder(h: PuzzleAdapter.PuzzleHolder, pos: Int) {
val p = puzzles[pos]
h.view.puzzleItem_text_ndx.text = "# " + p.descr()
h.view.puzzleItem_text_details.text = "Ndx: ${p.puzzleNdx}"
h.view.setOnClickListener {
Log.d("##", "PuzzleHolder.onClick (bunch#${p.parentBunch.bunchID}; puzzle#${p.puzzleNdx})")
val action = BunchFragmentDirections.navBunchToPuzzle(PuzzleParcel(p.parentBunch.bunchID, p.puzzleNdx))
it.findNavController().navigate(action)
}
}
inner class PuzzleHolder(v: View) : RecyclerView.ViewHolder(v) {
var view: View = v
}
}
PuzzleView ( пользовательский вид Я использую с адаптером):
class PuzzleView : RelativeLayout {
constructor (context: Context) : super(context) { init(context, null, 0) }
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { init(context, attrs, 0) }
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { init(context, attrs, defStyle) }
private fun init(context: Context, attrs: AttributeSet?, defStyle: Int) {
inflate(getContext(), R.layout.item_puzzle, this)
}
override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
//this.layout(l, t, r, b)
}
val rect = Rect(0, 0, 0, 0)
val paint = Paint()
override fun onDraw(canvas: Canvas) {
//super.onDraw(canvas)
Log.d("##", "PuzzleView.onDraw()")
rect.right = width - 10
rect.bottom = height - 10
val bkgrColor = ContextCompat.getColor(App.context, R.color.Green)
paint.style = Paint.Style.FILL
paint.color = bkgrColor
canvas.drawRect(rect, paint)
}
}
Несколько соображений по поводу вышеуказанного класса / кода:
override fun onLayout
требуется - У меня есть другие пользовательские представления, которые работают нормально (не в отношении RecyclerView) с пустым
onLayout
- Я пытался (это прокомментировано в приведенном выше коде)
this.layout(l, t, r, b)
, но получаю стекисключение переполнения
Мои единственные настоящие мысли здесь: (1) что-то, что я должен делать в этом onLayout
методе, но я не могу думать о чем;или (2) что-то не так с тем, как я надуваю item_puzzle
, но - опять же - я не могу думать о чем.(Я попробовал несколько вещей на этом, но безрезультатно.) Я не могу думать ни о чем другом!
И вот весь другой код, который, я думаю, мог бы быть уместным:
из фрагмента , содержащего RecyclerView (это то, что показано на трех изображениях выше):
bunch_recycler.layoutManager = GridLayoutManager(this.context, 3)
bunch_recycler.adapter = PuzzleAdapter(bunch.puzzles)
наконец, XML для сам элемент, item_puzzle :
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
app:cardBackgroundColor="@color/facadeLight"
app:cardElevation="0dp"
>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/shape_puzzle"
>
<TextView
android:id="@+id/puzzleItem_text_ndx"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginEnd="16dp"
android:ellipsize="end"
android:fontFamily="@font/showg"
android:maxLines="1"
android:text="17"
android:textColor="@color/facadeDark"
android:textSize="32sp"
/>
<TextView
android:id="@+id/puzzleItem_text_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/puzzleItem_text_ndx"
android:layout_marginStart="8dp"
android:text="details"
android:textSize="12sp"
/>
</RelativeLayout>
</androidx.cardview.widget.CardView>
ТАКЖЕ: если "пользовательское представление для разрешения onDraw" не является правильным(или лучший) способ достижения моей цели здесь, пожалуйста, дайте мне знать об этом.
Использование:
- Windows 10
- Android Studio 3.4
- Kotlin 1.3.31
- Gradle 3.4.0
- и следующие:
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0-alpha05'
implementation 'androidx.core:core-ktx:1.2.0-alpha01'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0-beta01'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-beta01'
implementation 'androidx.navigation:navigation-fragment-ktx:2.0.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.0.0'
Запоздалая мысль : Я видел (в 4 1/2-летнем видео youtube , переопределяющем getView
, чтобы назначить подпредставления в похожей ситуации. Нет способа getView
для переопределенияв сегодняшнем RecyclerView
.Я вижу, что это getItemViewType(position: Int): Int
, что тоже не кажется многообещающим (я немного читал об этом).Просто подумал, что упомяну об этом на случай, если это вызовет мысль для вас (где это не для меня)
Как всегда, спасибо за вашу помощь!