Приведите класс к его подклассу в Kotlin - PullRequest
1 голос
/ 30 марта 2020

У меня есть список класса с именем Opportunity, который заполнен объектами, расширяющими Opportunity. Список может иметь либо CommunityOpportunites, либо SponsorshipOpportunities.

Я переопределяю getItemViewType и присваиваю значение каждому элементу в зависимости от того, какой подкласс находится в соответствующем посте, и для каждого из них создается отдельный ViewHolder:

override fun getItemViewType(position: Int): Int {

    return if (opportunityList[position] is SponsorshipOpportunity){

        Log.i(TAG,"Item type is sponsorshipId")

        SPONSORSHIP

    } else{
        Log.i(TAG,"Item type is community")
        COMMUNITY

    }

}

    inner class CommunityViewHolder(var view: CommunityTileBinding):RecyclerView.ViewHolder(view.root)
    inner class SponsorshipViewHolder(var view: SponsorshipTileBinding):RecyclerView.ViewHolder(view.root)



companion object{
    private const val COMMUNITY = 0
    private const val SPONSORSHIP = 1
}

В onCreateViewHolder () я создаю правильный ViewHolder для класса элемента, а в onBindViewHolder () я пытаюсь привести элементы в списке (типа Opportunity в конструкторе) к подклассу элемента в представлении :

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {


    val inflater = LayoutInflater.from(parent.context)

    return when (viewType){

        COMMUNITY->CommunityViewHolder(DataBindingUtil.inflate(inflater, R.layout.community_tile, parent, false))
        SPONSORSHIP-> SponsorshipViewHolder(DataBindingUtil.inflate(inflater, R.layout.sponsorship_tile, parent, false))

        else-> throw IllegalArgumentException()


    }
}

override fun getItemCount(): Int = opportunityList.size


override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {

  when(holder.itemViewType){
      COMMUNITY-> {

          (holder as CommunityViewHolder).view.communityOpportunity = opportunityList[position] as CommunityOpportunity
      }
      SPONSORSHIP->{
          (holder as SponsorshipViewHolder).view.sponsorship = opportunityList[position] as SponsorshipOpportunity
          holder.view.postActionText.text = context.resources.getString(R.string.watch_respond)

      }
  }

}

Тем не менее, я получаю следующее исключение приведения класса

java.lang.ClassCastException: com.example.model.Opportunity cannot be cast to com.example.model.CommunityOpportunity

, когда я пытаюсь выполнить соответствующую строку в onBindViewHolder, даже если оператор журнала подтверждает, что элемент является CommunityOpportunity в getItemViewType () печатается.

Есть ли лучший способ задать вопрос или есть лучший способ для меня отображать несколько типов ViewHolder / Object в RecyclerView?

Редактировать: Здесь соответствующие xml макеты:

xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<data>
    <variable
        name="sponsorship"
        type="com.example.weare8sample.model.SponsorshipOpportunity"/>
</data>

<LinearLayout

    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:elevation="2dp"
    android:orientation="vertical"
    android:background="@drawable/tile_background">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:foreground="@drawable/tile_background"
        android:imageUrl="@{sponsorship.coverTileUri}">

    </ImageView>



    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:gravity="center_vertical"
        android:padding="10dp">

        <TextView
            android:id="@+id/postActionText"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:fontFamily="@font/lato_bold"
            tools:text="@string/watch_respond">

        </TextView>

        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:src="@drawable/ic_chevron_right_black_36dp">

        </ImageView>



    </RelativeLayout>

</LinearLayout>

xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<data>

    <variable
        name="communityOpportunity"
        type="com.example.weare8sample.model.CommunityOpportunity" />
</data>


<ImageView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="10dp"
    android:background="@drawable/tile_background"
    android:imageUrl="@{communityOpportunity.mediaImageUri}">

</ImageView>

Ответы [ 2 ]

1 голос
/ 30 марта 2020

Добавьте явную проверку типов для всех типов в getItemViewType и бросьте, если это неизвестный тип, чтобы правильно обрабатывать все случаи.

Как и сейчас, если есть 3-й тип Возможности, предполагается, что он имеет тип COMMUNITY.

0 голосов
/ 30 марта 2020

Используйте закрытый класс в вашей модели, который является лучшим вариантом для этого случая

sealed class Opportunity {
  data class CommunityOpportunites( 
                 // class fields
             ):Opportunity()
  data class SponsorshipOpportunities(
                                   // class fields
                                      ):Opportunity()
}

, а в вашем методе getItemViewType должен быть такой

override fun getItemViewType(position: Int) = when (list[position]) {
    is Opportunity.CommunityOpportunites-> 0
    is Opportunity.SponsorshipOpportunities-> 1
}

и метод onCreateViwHolder

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = 
    when (viewType) {
    0 -> ViewHolder(
        LayoutInflater.from(parent.context).inflate(R.layout.item1, parent, 
        false)
    )
    else-> 
     LayoutInflater.from(parent.context).inflate(R.layout.item2, parent, 
           false)
    )
}

вам не нужно использовать несколько ViwHolders для записи внутреннего класса ViewHolder, как эти

inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
    fun bind() {
        val d = list[adapterPosition]
        when (d) {
            is Opportunity.CommunityOpportunites -> {
                itemView.apply {
                    //bind the data in the list to view you needn't cast d to 
                    //CommunityOpportunites Kotlin smart cast does it for you
                }
            }
            is Opportunity.SponsorshipOpportunities -> {
                itemView.apply {
                    //bind the data in the list to view you needn't cast d to 
                    //SponsorshipOpportunities Kotlin smart cast does it for you
                }
            }
        }
    }
}

Я думаю, что это должно работать:)

...