Daniel Gomez Rico
Daniel Gomez Rico

Reputation: 15946

Using weight on RecyclerView the items width starts behaving weird

Whats wrong with the next layouts?

I pretend to include a RecyclerView inside a DialogFragment to list Checkboxes and handle the check.

Issues:

  1. Item text should fill the content of the screen.
  2. On scroll the items arrange to the right position.

dialog.xml:

<LinearLayout 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:orientation="vertical"
  android:paddingLeft="@dimen/spacing_large"
  android:paddingRight="@dimen/spacing_large"
  android:paddingTop="@dimen/spacing_large">

  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/degrees_picker"
    android:textAppearance="@style/TextAppearance.AppCompat.Title" />

  <ProgressBar
    android:id="@+id/degreesProgressBar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_horizontal"
    android:layout_margin="@dimen/spacing_large"
    android:indeterminate="true"
    android:visibility="gone" />

  <android.support.v7.widget.RecyclerView
    android:id="@+id/degreesRecyclerView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_marginTop="@dimen/spacing_medium"
    android:layout_weight="1"
    tools:listitem="@layout/degrees_item" />

  <LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="right">

    <Button
      android:id="@+id/degreesCancelButton"
      style="@style/Widget.AppCompat.Button.Borderless.Colored"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@string/cancel" />

    <Button
      android:id="@+id/degreesOkButton"
      style="@style/Widget.AppCompat.Button.Borderless.Colored"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="@string/ok" />

  </LinearLayout>
</LinearLayout>

degrees_item.xml (if this just have a CheckBox same thing happens):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:minHeight="?android:attr/listPreferredItemHeightSmall"
  android:orientation="horizontal">

  <TextView
    android:id="@+id/degreesItemTextView"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical"
    android:layout_weight="1"
    android:gravity="center_vertical"
    tools:text="Degree tal tal tal tal tal tal tal tal tal tal tal tal tal tal tal tal" />

  <CheckBox
    android:id="@+id/degreesItemCheck"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center_vertical"
    android:layout_marginLeft="@dimen/spacing_medium" />

</LinearLayout>

adapter:

class DegreesAdapter(val preselectedItems: List<Degree>?) : RecyclerView.Adapter<DegreesAdapter.ViewHolder>(), SubtopicsItemListener {
  var items = listOf<Degree>()
    set(values) {
      field = values.map { CheckableDegree(it, checked = preselectedItems?.contains(it) == true) }
      notifyDataSetChanged()
    }

  val selectedItems get() = items.filter { (it as? CheckableDegree)?.checked == true }

  override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
    val inflater = LayoutInflater.from(parent?.context)
    val contactView = inflater.inflate(R.layout.degrees_item, parent, false)

    return ViewHolder(contactView, this)
  }

  override fun getItemCount() = items.size

  override fun getItemId(position: Int): Long {
    return items[position].id.toLong()
  }

  override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
    val degree = items[position]

    if (degree is CheckableDegree) {
      holder?.itemView?.degreesItemCheck?.isChecked = degree.checked
    }

    holder?.itemView?.degreesItemTextView?.text = degree.description
  }

  override fun onItemCheck(position: Int, checked: Boolean) {
    (items[position] as? CheckableDegree)?.checked = checked
  }

  class ViewHolder(itemView: View, listener: SubtopicsItemListener) : RecyclerView.ViewHolder(itemView) {
    init {
      itemView.degreesItemCheck.setOnCheckedChangeListener { _, isChecked ->
        listener.onItemCheck(layoutPosition, isChecked)
      }
    }
  }
}

interface SubtopicsItemListener {
  fun onItemCheck(position: Int, checked: Boolean)
}

class CheckableDegree(degree: Degree, var checked: Boolean = false)
  : Degree(degree.id, degree.description), Parcelable {
  companion object {
    @JvmField val CREATOR: Parcelable.Creator<CheckableDegree> = object : Parcelable.Creator<CheckableDegree> {
      override fun createFromParcel(source: Parcel): CheckableDegree = CheckableDegree(source)
      override fun newArray(size: Int): Array<CheckableDegree?> = arrayOfNulls(size)
    }
  }

  constructor(source: Parcel)
      : this(source.readParcelable<Degree>(Degree::class.java.classLoader),
      1 == source.readInt())

  override fun describeContents() = 0

  override fun writeToParcel(dest: Parcel, flags: Int) {
    super.writeToParcel(dest, flags)
    dest.writeInt((if (checked) 1 else 0))
  }
}

DegreesDialogFragment:

import android.os.Bundle
import android.support.v4.app.DialogFragment
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.medanswers.R
import com.medanswers.api.error.ApiError
import com.medanswers.api.repositories.user.Degree
import com.medanswers.base.MyActivity
import kotlinx.android.synthetic.main.degrees_dialog.*
import javax.inject.Inject

/**
 * Show the lists of subtopics
 */
class DegreesDialogFragment : DialogFragment(), DegreesDialogContract.View {

  val listener: Listener?
    get() = (activity as? Listener) ?: (targetFragment as? Listener)

  @Inject lateinit var presenter: DegreesDialogContract.Presenter

  companion object {
    val extrasPreselectedItems = "preselected_items"

    fun newInstance(preselectedItems: List<Degree>?): DegreesDialogFragment {
      val frag = DegreesDialogFragment()
      frag.arguments = Bundle().apply {
        preselectedItems?.let { putParcelableArray(extrasPreselectedItems, it.toTypedArray()) }
      }
      return frag
    }
  }

  override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, state: Bundle?): View? {
    return inflater?.inflate(R.layout.degrees_dialog, container)
  }

  override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    (activity as MyActivity).activityComponent.inject(this)

    @Suppress("UNCHECKED_CAST")
    val preselectedItems =
        (arguments.getParcelableArray(extrasPreselectedItems) as? Array<Degree>)?.toList()

    // calls initViews(preselectedItems)
    presenter.attach(this, preselectedItems)
  }

  override fun initViews(preselectedItems: List<Degree>?) {
    degreesRecyclerView.apply {
      layoutManager = LinearLayoutManager(activity)
      adapter = DegreesAdapter(preselectedItems)
    }

    degreesOkButton.setOnClickListener {
      (degreesRecyclerView?.adapter as? DegreesAdapter)?.selectedItems?.let {
        listener?.onDegreesDialogOkClick(it)
        dismiss()
      }
    }

    degreesCancelButton.setOnClickListener { [email protected]() }
  }

  override fun showItems(degrees: List<Degree>) {
    (degreesRecyclerView?.adapter as? DegreesAdapter)?.items = degrees
  }

  override fun showProgress() {
    degreesProgressBar.visibility = View.VISIBLE
  }

  override fun dismissProgress() {
    degreesProgressBar.visibility = View.GONE
  }

  override fun close(apiError: ApiError?) {
    apiError?.let { listener?.onDegreesDialogError(it) }
    dismiss()
  }

  interface Listener {
    fun onDegreesDialogOkClick(degrees: List<Degree>)
    fun onDegreesDialogError(apiError: ApiError)
  }
}

enter image description here

Upvotes: 0

Views: 1932

Answers (1)

Kingfisher Phuoc
Kingfisher Phuoc

Reputation: 8200

You need to set up your DialogFragment width from code like below:

    WindowManager.LayoutParams p = getDialog().getWindow().getAttributes();
    p.width = ViewGroup.LayoutParams.MATCH_PARENT;
    p.height = ViewGroup.LayoutParams.MATCH_PARENT;
    getDialog().getWindow().setGravity(Gravity.CENTER);
    getDialog().getWindow().setAttributes(p);

You can take a look at my EasyFilePickerDialog, and check the file FilePickerDialogFragment.java for more detail.

Upvotes: 1

Related Questions