1. 基于 保存到项目 修改和添加
保存到项目https://blog.csdn.net/u011193452/article/details/127065427
2. 适配器 Cell 布局
2.1 提示布局 gallery_footer.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="30dp"
android:indeterminateTint="?attr/colorPrimary" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在加载" />
</LinearLayout>
2.2 cell 布局 gallery_cell.xml
<?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"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
app:cardCornerRadius="4dp">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/imageView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="@+id/textViewLikes"
app:layout_constraintStart_toEndOf="@+id/textViewLikes"
app:layout_constraintTop_toTopOf="@+id/textViewLikes"
app:srcCompat="@drawable/ic_baseline_favorite" />
<TextView
android:id="@+id/textViewLikes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="TextView"
android:textSize="10sp"
app:layout_constraintBottom_toBottomOf="@+id/imageView3"
app:layout_constraintStart_toEndOf="@+id/imageView3"
app:layout_constraintTop_toTopOf="@+id/imageView3" />
<ImageView
android:id="@+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:srcCompat="@drawable/ic_baseline_thumb_up" />
<TextView
android:id="@+id/textViewUser"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:text="TextView"
android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@+id/imageView3"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/shimmerLayoutCell" />
<TextView
android:id="@+id/textViewFavorites"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="TextView"
android:textSize="10sp"
app:layout_constraintBottom_toBottomOf="@+id/imageView5"
app:layout_constraintStart_toEndOf="@+id/imageView5"
app:layout_constraintTop_toTopOf="@+id/imageView5" />
<io.supercharge.shimmerlayout.ShimmerLayout
android:id="@+id/shimmerLayoutCell"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
tools:srcCompat="@tools:sample/avatars" />
</io.supercharge.shimmerlayout.ShimmerLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
3. 修改适配器 GalleryAdapter.kt
class GalleryAdapter(private val galleryViewModel: GalleryViewModel) : ListAdapter<PhotoItem, MyViewHolder>(DIFFCALLBACK) {
// 创建类的一个常量,相当与 java 的静态域
companion object {
const val NORMAL_VIEW_TYPE = 0
const val FOOTER_VIEW_TYPE = 1
}
var footerViewStatus = DATA_STATUS_CAN_LOAD_MORE
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val holder: MyViewHolder
if (viewType == NORMAL_VIEW_TYPE) {
holder = MyViewHolder(
GalleryCellBinding.inflate(LayoutInflater.from(parent.context), parent, false)
)
holder.itemView.setOnClickListener {
Bundle().apply {
//putParcelable("PHOTO", getItem(holder.adapterPosition))
//holder.itemView.findNavController().navigate(R.id.action_galleryFragment_to_photoFragment,this)
putParcelableArrayList("PHOTO_LIST", ArrayList(currentList))
putInt("PHOTO_POSITION", holder.adapterPosition)
holder.itemView.findNavController()
.navigate(R.id.action_galleryFragment_to_pagerPhotoFragment, this)
}
}
} else {
holder = MyViewHolder(GalleryFooterBinding.inflate(
LayoutInflater.from(parent.context), parent, false
).also {
(it.root.layoutParams as StaggeredGridLayoutManager.LayoutParams).isFullSpan = true
it.root.setOnClickListener { _ ->
it.textView.text = "正在加载"
it.progressBar.visibility = View.VISIBLE
galleryViewModel.fetchData()
}
})
}
return holder
}
override fun getItemViewType(position: Int): Int {
return if (position == itemCount - 1) FOOTER_VIEW_TYPE else NORMAL_VIEW_TYPE
}
override fun getItemCount(): Int {
return super.getItemCount() + 1
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
if (position == itemCount - 1) {
val footerBinding = holder.mViewBinding as GalleryFooterBinding
with(footerBinding) {
when (footerViewStatus) {
DATA_STATUS_CAN_LOAD_MORE -> {
progressBar.visibility = View.VISIBLE
textView.text = "正在加载"
footerBinding.root.isClickable = false
}
DATA_STATUS_NO_MORE -> {
progressBar.visibility = View.GONE
textView.text = "全部加载完毕"
footerBinding.root.isClickable = false
}
DATA_STATUS_NETWORK_ERROR -> {
progressBar.visibility = View.GONE
textView.text = "网络故障,稍后重试"
footerBinding.root.isClickable = true
}
}
}
return
}
val photoItem = getItem(position)
val cellBinding = holder.mViewBinding as GalleryCellBinding
with(cellBinding) {
shimmerLayoutCell.apply {
setShimmerColor(0x55FFFFFF)
setShimmerAngle(0)
startShimmerAnimation()
}
textViewUser.text = photoItem.photoUser
textViewLikes.text = photoItem.photoLikes.toString()
textViewFavorites.text = photoItem.photoFavorites.toString()
imageView.layoutParams.height = photoItem.photoHeight
}
Glide.with(holder.itemView)
.load(getItem(position).previewUrl)
.placeholder(R.drawable.ic_photo_place_holder)
.listener(object : RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
return false.also { cellBinding.shimmerLayoutCell.stopShimmerAnimation() }
}
}).into(cellBinding.imageView)
}
object DIFFCALLBACK : DiffUtil.ItemCallback<PhotoItem>() {
override fun areItemsTheSame(oldItem: PhotoItem, newItem: PhotoItem): Boolean {
return oldItem.photoId == newItem.photoId
}
override fun areContentsTheSame(oldItem: PhotoItem, newItem: PhotoItem): Boolean {
return oldItem == newItem
}
}
}
class MyViewHolder(binding: ViewBinding) : ViewHolder(binding.root) {
val mViewBinding = binding
}
4. 修改 ViewModel,GalleryViewModel.kt
//全局的常量
const val DATA_STATUS_CAN_LOAD_MORE = 0
const val DATA_STATUS_NO_MORE = 1
const val DATA_STATUS_NETWORK_ERROR = 2
class GalleryViewModel(application: Application) : AndroidViewModel(application) {
private val TAG = GalleryViewModel::class.java.simpleName
private val _dataStatusLive = MutableLiveData<Int>()
val dataStatusLive: LiveData<Int>
get() = _dataStatusLive
private var _photoListLive = MutableLiveData<List<PhotoItem>>()
val photoListLive: LiveData<List<PhotoItem>>
get() = _photoListLive
var needToScrollToTop = true
private var perPage = 20
private val keyWords =
arrayOf("cat", "car", "beauty", "dog", "phone", "computer", "flower", "animal")
private var currentPage = 1
private var totalPage = 1
private var currentKey = "cat"
private var isNewQuery = true
private var isLoading = false
init {
resetQuery()
}
fun resetQuery() {
currentPage = 1
totalPage = 1
currentKey = keyWords.random()
isNewQuery = true
needToScrollToTop = true
fetchData()
}
fun fetchData() {
if (isLoading) return
if(currentPage > totalPage){
_dataStatusLive.value = DATA_STATUS_NO_MORE
return
}
isLoading = true
val stringRequest = StringRequest(
Request.Method.GET,
getUrl(),
{
with(Gson().fromJson(it, Pixabay::class.java)) {
totalPage = ceil(totalHits.toDouble() / perPage).toInt()
if (isNewQuery) {
_photoListLive.value = hits.toList()
} else {
_photoListLive.value = arrayListOf(_photoListLive.value!!, hits.toList()).flatten()
}
//Log.i("MyTag", "totalHits $totalHits")
_dataStatusLive.value = DATA_STATUS_CAN_LOAD_MORE
isLoading = false
isNewQuery = false
currentPage++
}
}, {
_dataStatusLive.value = DATA_STATUS_NETWORK_ERROR
isLoading = false
Log.e(TAG, "onErrorResponse", it)
})
VolleySingleton.getInstance(getApplication()).requestQueue.add(stringRequest)
}
private fun getUrl(): String {
//&per_page=100 &pretty=true
return "https://pixabay.com/api/?key=30070990-cfc31c9f778ceeef4009d910d&q=${currentKey}&per_page=${perPage}&page=${currentPage}"
}
}
5. 调用页 GalleryFragment.kt
class GalleryFragment : Fragment() {
private lateinit var binding: FragmentGalleryBinding
private lateinit var galleryViewModel: GalleryViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View? {
binding = FragmentGalleryBinding.inflate(inflater, container, false)
return binding.root
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.swipeIndicator -> {
binding.swipeLayoutGallery.isRefreshing = true
Handler().postDelayed(Runnable { galleryViewModel.resetQuery() }, 700)
}
}
return super.onOptionsItemSelected(item)
}
//加载菜单栏
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.menu, menu)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setHasOptionsMenu(true)
//,ViewModelProvider.AndroidViewModelFactory(requireActivity().application)
galleryViewModel = ViewModelProvider(this)[GalleryViewModel::class.java]
val galleryAdapter = GalleryAdapter(galleryViewModel)
binding.recyclerView?.apply {
adapter = galleryAdapter
//GridLayoutManager(requireContext(), 2) 对齐 / StaggeredGridLayoutManager 交错感
layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL)
//layoutManager = GridLayoutManager(requireContext(), 2)
}
//加载数据监听
galleryViewModel.photoListLive.observe(viewLifecycleOwner, Observer {
if(galleryViewModel.needToScrollToTop){
binding.recyclerView.scrollToPosition(0)
galleryViewModel.needToScrollToTop = false
}
galleryAdapter.submitList(it)
binding.swipeLayoutGallery.isRefreshing = false
})
//数据状态监听
galleryViewModel.dataStatusLive.observe(viewLifecycleOwner, Observer {
galleryAdapter.footerViewStatus = it
galleryAdapter.notifyItemChanged(galleryAdapter.itemCount-1)
if (it == DATA_STATUS_NETWORK_ERROR) binding.swipeLayoutGallery.isRefreshing = false
})
binding.swipeLayoutGallery.setOnRefreshListener {
galleryViewModel.resetQuery()
}
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
if (dy < 0) return
val layoutManager = recyclerView.layoutManager as StaggeredGridLayoutManager
val intArray = IntArray(2)
layoutManager.findLastVisibleItemPositions(intArray)
if (intArray[0] == galleryAdapter.itemCount - 1) {
galleryViewModel.fetchData()
}
}
})
}
}
6. 展示图