如何使RecyclerView焦点项目固定在屏幕上

时间:2019-05-21 20:21:28

标签: android android-recyclerview android-linearlayout

我已经建立了一个无尽的水平滚动适配器,该适配器可以从api获取分页数据。 现在,我正在尝试使当前关注的项保持在特定的x值,并使其余子项滚动到该值。像这样: [the goal] 我试过从LinearSnapHelper派生并覆盖一些方法,但我认为我什么也没找到。

这是Horizo​​ntal RecyclerView布局和相应的适配器类:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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/media_list_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/selected_background_color"
android:baselineAligned="false"
android:descendantFocusability="beforeDescendants"
android:focusable="false"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/selected_content_description">

    <TextView
        android:id="@+id/media_list_title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/default_background"
        android:gravity="start|center_horizontal"
        android:padding="10dp"
        android:paddingTop="0dp"
        android:text="@string/media_list_1_title" />

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/media_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@drawable/trending_background"
        android:fadingEdge="horizontal"
        android:fadingEdgeLength="40dp"
        android:focusable="true"
        android:orientation="horizontal"
        android:paddingStart="20dp"
        android:paddingEnd="20dp"
        android:paddingBottom="10dp" />

</LinearLayout>

class TMDBMovieItemListAdapter(val activity: Activity, val layoutManager: LinearLayoutManager, val endpoint: TMDBAPI) : RecyclerView.Adapter<TMDBMovieItemListAdapter.MovieItemViewHolder>() {

lateinit var recyclerView : RecyclerView
var fetcher: FetchMovieInfoListFromTMDB = FetchMovieInfoListFromTMDB(this, endpoint)

private var data: Array<String> = arrayOf("Loading Titles")
private var movieData: Array<Movie> = arrayOf(Movie.DEFAULT)
private var fullBaseUrl: String

private var page = 0
private var maxPage = 1000

private var screenWidth: Int
private var screenHeight: Int

init {
    val displayMetrics = DisplayMetrics()
    activity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics)
    screenHeight = displayMetrics.heightPixels
    screenWidth = displayMetrics.widthPixels

    loadNextPage()

    val widthParam = activity.getPreferences(Context.MODE_PRIVATE).getString(activity.getString(R.string.saved_image_width_param_key), "w300")
    val secureBaseUrl = activity.getPreferences(Context.MODE_PRIVATE).getString(activity.getString(R.string.saved_image_url_key), "https://image.tmdb.org/t/p/")
    fullBaseUrl = secureBaseUrl + widthParam
}

class MovieItemViewHolder(linearLayout: CardView) : RecyclerView.ViewHolder(linearLayout) {

    lateinit var movie: Movie

    init {
        itemView.setOnClickListener(object : View.OnClickListener {
            override fun onClick(p0: View?) {
                Log.i("Movie Item Adapter", "Clicked " + p0?.findViewById<TextView>(R.id.movie_list_adapter_item_id)?.text)
            }
        })
    }
}

fun replaceData(data: Array<Movie>) {
    this.movieData = data
    Log.i("Adapter replaceData", "Length of data array: " + this.movieData.size)
    this.notifyDataSetChanged()
}

fun updateData(newData: Array<Movie>) {
    if (this.movieData.size == 1) {
        replaceData(newData)
        return
    }

    val oldMaxIndex = this.movieData.lastIndex

    this.movieData = this.movieData.plus(newData)
    this.notifyItemRangeInserted(oldMaxIndex, newData.size)
}

override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
    this.recyclerView = recyclerView

    //Add infinite scrolling and pagination
    recyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener() {
        var previousTotal = 0
        var loading = true
        val visibleThreshold = 10
        var firstVisibleItem = 0
        var visibleItemCount = 0
        var totalItemCount = 0

        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            visibleItemCount = layoutManager.childCount
            totalItemCount = layoutManager.itemCount
            firstVisibleItem = layoutManager.findFirstCompletelyVisibleItemPosition()

            Log.i("onScrolled", "visibleItemCount: " + visibleItemCount + "\ntotalItemCount: " + totalItemCount + "\nfirstVisibleItem: " + firstVisibleItem)

            if (loading) {
                if (totalItemCount > previousTotal) {
                    loading = false
                    previousTotal = totalItemCount
                }
            }

            if (!loading && (totalItemCount - visibleItemCount) <= (firstVisibleItem + visibleThreshold)) {
                loadNextPage()
                loading = true
            }

            super.onScrolled(recyclerView, dx, dy)
        }
    })

    super.onAttachedToRecyclerView(recyclerView)
}

private fun selectMiddleItem() {
    val firstVisibleIndex = layoutManager.findFirstVisibleItemPosition()
    val lastVisibleIndex = layoutManager.findLastVisibleItemPosition()
    val visibleIndexes = listOf(firstVisibleIndex..lastVisibleIndex).flatten()

    for (i in visibleIndexes) {
        val v = layoutManager.findViewByPosition(i)
        if (v == null) {
            continue
        }
        val location = IntArray(2)
        v.getLocationOnScreen(location)
        val x = location[0]
        val halfWidth = v.width * .5
        val rightSide = x + halfWidth
        val leftSide = x - halfWidth
        val isInMiddle = screenWidth * .5 in leftSide..rightSide
        if (isInMiddle) {
            // "i" is your middle index and implement selecting it as you want
            layoutManager.scrollToPositionWithOffset(i, 0)
            return
        }
    }
}

//Create new views (invoked by the layout manager)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieItemViewHolder {
    val layout = LayoutInflater.from(parent.context)
            .inflate(R.layout.movie_list_item, parent, false) as CardView
    // set the view's size, margins, paddings and layout parameters...
    return MovieItemViewHolder(layout)
}

//replace the contents of a view (it got scrolled on screen or something)
override fun onBindViewHolder(holder: MovieItemViewHolder, position: Int) {
    val data = movieData[position]
    val backgroundImageView = holder.itemView.findViewById<ImageView>(R.id.movie_list_item_background_image)
    val textView = holder.itemView.findViewById<TextView>(R.id.movie_list_adapter_item_id)
    val fullPosterLink = fullBaseUrl + data.backdropImageUrl
    val progressIcon = CircularProgressDrawable(activity)
    progressIcon.arrowEnabled = true
    progressIcon.setArrowDimensions(12f, 6f)
    progressIcon.centerRadius = 60f
    progressIcon.strokeWidth = 8f

    if (!data.posterImageUrl.isNullOrBlank()) {
        Picasso.get().load(fullPosterLink).placeholder(progressIcon).fit().into(backgroundImageView)
    }

    textView.text = data.title
}

override fun getItemCount() = movieData.size

private fun loadNextPage() {
    if (fetcher.status == AsyncTask.Status.RUNNING) return
    fetcher = FetchMovieInfoListFromTMDB(this, endpoint)

    page++
    if (page >= maxPage) return

    endpoint.addProperty(Pair("page", page.toString()))
    fetcher.execute()
}
}

我最初尝试使用selectMiddleItem()类,但是它似乎干扰了我无穷无尽的滚动逻辑,并且它只会不断地随机滚动。看起来我还需要一个AC回调,它在recyclerview内的子对象之间更改焦点之后被调用,但是似乎没有。

0 个答案:

没有答案