我已经建立了一个无尽的水平滚动适配器,该适配器可以从api获取分页数据。 现在,我正在尝试使当前关注的项保持在特定的x值,并使其余子项滚动到该值。像这样: [] 我试过从LinearSnapHelper派生并覆盖一些方法,但我认为我什么也没找到。
这是Horizontal 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内的子对象之间更改焦点之后被调用,但是似乎没有。