Android 仿京東分類功能實現(xiàn)

業(yè)務(wù)需要實現(xiàn)類似京東分類頁面的頁面效果,原先沒有寫過類似的這種效果,特此搞個Demo 梳理一下整體的實現(xiàn)流程以及記錄下邏輯實現(xiàn)的效果

一,效果

效果.gif

二,實現(xiàn)思路 RecycleView+Fragment(RecycleView)+觀察

  .

  1:左側(cè)是RecycleView 為了實現(xiàn)局部刷新 使用了DiffUtil   處理局部刷新問題

  2:右側(cè)是一個Fragment 用了 SmartRefreshLayout,RecycleView處理刷新效果

  3.數(shù)據(jù)通訊使用了 ClassificationObservable,ClassificationFinshObservable兩個觀察者處理開始刷新以及刷新結(jié)束的狀態(tài)

  .

三,代碼實現(xiàn)

1.首頁代碼

package com.wu.third.classification

import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.wu.base.adapter.KtAdapter
import com.wu.third.R
import com.wu.third.databinding.ActivityClassificationBinding
import java.util.*
import kotlin.collections.ArrayList


class ClassificationActivity : AppCompatActivity(), Observer {

 var binding: ActivityClassificationBinding? = null
 //右側(cè)內(nèi)容
 var currentFragment: ClassificationContentFragment? = null
 //左側(cè)內(nèi)容適配器
 var tagAdapter: ClassificationTagAdapter? = null
 //靜態(tài)圖片
 var imgs = arrayListOf<String>(
     "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=0b42192b10b6d521d58cd0650a0148e6",
     "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F17%2F09%2F15%2F67351408baad11ce25c9b14166a049a6.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=a6c67c2f08b86a82348b2c87e75ffd9c",
     "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Ftupian.qqjay.com%2Fu%2F2018%2F0222%2F2_163119_13.jpg&refer=http%3A%2F%2Ftupian.qqjay.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=995f87498f2d19ab6c885e09904d1ef3",
     "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fdpic.tiankong.com%2Ftc%2Feb%2FQJ9124407543.jpg&refer=http%3A%2F%2Fdpic.tiankong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=c76415814c9c52ae20480a89199a128c",
     "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fdpic.tiankong.com%2Ftc%2Feb%2FQJ9124407543.jpg&refer=http%3A%2F%2Fdpic.tiankong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=c76415814c9c52ae20480a89199a128c",
     "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.pconline.com.cn%2Fimages%2Fupload%2Fupc%2Ftx%2Fitbbs%2F1705%2F10%2Fc35%2F46579542_1494425402317_mthumb.jpg&refer=http%3A%2F%2Fimg.pconline.com.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=3ddc7531c2a48fd48712346466ebd228",
     "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2Ftp01%2F1ZZQ20QJS6-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=5f6463e28700d48143f6be2c554f8a28"
 )
 //左側(cè)商品分類
 var tags = arrayListOf<ClassificationTagInfo>(
     ClassificationTagInfo("家電", true),
     ClassificationTagInfo("食品", false),
     ClassificationTagInfo("服裝", false),
     ClassificationTagInfo("電子", false),
     ClassificationTagInfo("農(nóng)產(chǎn)品", false),
     ClassificationTagInfo("飲料", false),
 )

 override fun onCreate(savedInstanceState: Bundle?) {
     super.onCreate(savedInstanceState)
     binding = DataBindingUtil.setContentView<ActivityClassificationBinding>(
         this,
         R.layout.activity_classification
     )
     ClassificationObservable.addObserver(this)
     ClassificationFinshObservable.addObserver(this)
     initView()
 }




 private fun initView() {
     tagAdapter = ClassificationTagAdapter(this)
     binding!!.rvClassify.layoutManager = LinearLayoutManager(this)
     binding!!.rvClassify.adapter = tagAdapter
     tagAdapter!!.setAsyncListDatas(tags)
     currentFragment = ClassificationContentFragment.newInstance(0)
     supportFragmentManager.beginTransaction().replace(R.id.fragment_content, currentFragment!!) .commit()

     tagAdapter!!.setOnViewClickListener(object :
         KtAdapter.OnAdapterViewClickListener<ClassificationTagInfo> {
         override fun onViewClick(v: View?, program: ClassificationTagInfo?) {
             var position = tagAdapter!!.getAsyncListItems()!!.indexOf(program)
             currentFragment!!.setData(getContentData(position), position)
             processFinish(position)
         }
     })
 }

 fun getContentData(position: Int): ArrayList<ClassificationContentInfo> {
     var contentInfos = ArrayList<ClassificationContentInfo>()
     var contentInfo = ClassificationContentInfo()
     contentInfo.title = tags.get(position).title
     contentInfo.imgs = imgs
     contentInfos.add(contentInfo)
     return contentInfos
 }

 override fun onDestroy() {
     super.onDestroy()
     ClassificationObservable.deleteObserver(this)
     ClassificationFinshObservable.deleteObserver(this)
 }

 //右側(cè)刷新的觀察者
 private fun processRefreshLoadMoreData(info: ClassificationObservableInfo) {
     if ((info.type == 0 && info.position == 0) || (info.type == 1 && info.position >= tags.size - 1)) {
         currentFragment!!.finish()
         return
     }
     if (info.type == 0) {
         //下拉 刷新
         info.position -= 1
     } else {
         //上拉加載
         info.position += 1
     }
     currentFragment!!.setData(getContentData(info.position),  info.position)
 }
 //刷新完成后改變 左側(cè)標(biāo)題數(shù)據(jù)
 private fun processFinish(position: Int) {
     var newList = ArrayList<ClassificationTagInfo>()
     var oldList = tagAdapter!!.getAsyncListItems()
     oldList.forEachIndexed { index, classificationTagInfo ->
         var info: ClassificationTagInfo? = null
         if (index == position) {
             info = ClassificationTagInfo(classificationTagInfo.title, true)
         } else {
             info = ClassificationTagInfo(classificationTagInfo.title, false)
         }
         newList.add(info)
     }
     tagAdapter!!.setAsyncListDatas(newList)
 }


 override fun update(o: Observable?, arg: Any?) {
     if (o is ClassificationObservable) {
         var info = arg as ClassificationObservableInfo
         processRefreshLoadMoreData(info)
     } else if (o is ClassificationFinshObservable) {
         processFinish(arg as Int)
     }
 }


}

1.2.layout

<?xml version="1.0" encoding="utf-8"?>
<layout>


    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_classify"
            android:layout_width="100dp"
            android:layout_height="wrap_content" />

        <FrameLayout
            android:id="@+id/fragment_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_toRightOf="@+id/rv_classify" />

    </RelativeLayout>

</layout>

2.右側(cè)Fragment實現(xiàn)

package com.wu.third.classification

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.scwang.smartrefresh.layout.api.RefreshLayout
import com.scwang.smartrefresh.layout.listener.OnRefreshLoadMoreListener
import com.wu.third.R
import com.wu.third.databinding.FragmentClassificationContentBinding


/**
* @author wkq
*
* @date 2021年11月18日 16:06
*
*@des
*
*/

class ClassificationContentFragment : Fragment() {
  //設(shè)置出艱苦
  var imgs = arrayListOf<String>(
      "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F16%2F10%2F29%2F2ac8e99273bc079e40a8dc079ca11b1f.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=0b42192b10b6d521d58cd0650a0148e6",
      "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fbpic.588ku.com%2Felement_origin_min_pic%2F17%2F09%2F15%2F67351408baad11ce25c9b14166a049a6.jpg&refer=http%3A%2F%2Fbpic.588ku.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=a6c67c2f08b86a82348b2c87e75ffd9c",
      "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Ftupian.qqjay.com%2Fu%2F2018%2F0222%2F2_163119_13.jpg&refer=http%3A%2F%2Ftupian.qqjay.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=995f87498f2d19ab6c885e09904d1ef3",
      "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Ftupian.qqjay.com%2Fu%2F2018%2F0222%2F2_163119_13.jpg&refer=http%3A%2F%2Ftupian.qqjay.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=995f87498f2d19ab6c885e09904d1ef3",
      "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Ftupian.qqjay.com%2Fu%2F2018%2F0222%2F2_163119_13.jpg&refer=http%3A%2F%2Ftupian.qqjay.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=995f87498f2d19ab6c885e09904d1ef3",
      "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Ftupian.qqjay.com%2Fu%2F2018%2F0222%2F2_163119_13.jpg&refer=http%3A%2F%2Ftupian.qqjay.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=995f87498f2d19ab6c885e09904d1ef3",
      "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fdpic.tiankong.com%2Ftc%2Feb%2FQJ9124407543.jpg&refer=http%3A%2F%2Fdpic.tiankong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=c76415814c9c52ae20480a89199a128c",
      "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.pconline.com.cn%2Fimages%2Fupload%2Fupc%2Ftx%2Fitbbs%2F1705%2F10%2Fc35%2F46579542_1494425402317_mthumb.jpg&refer=http%3A%2F%2Fimg.pconline.com.cn&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=3ddc7531c2a48fd48712346466ebd228",
      "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2Ftp01%2F1ZZQ20QJS6-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1639813297&t=5f6463e28700d48143f6be2c554f8a28"
  )
  var binding: FragmentClassificationContentBinding? = null
  var position = 0;
  var finisTime: Long = 1000

  companion object {
      fun newInstance(position: Int): ClassificationContentFragment {
          val args = Bundle()
          args.putInt("position", position)
          val fragment = ClassificationContentFragment()
          fragment.arguments = args
          return fragment
      }
  }

  override fun onCreateView(
      inflater: LayoutInflater,
      container: ViewGroup?,
      savedInstanceState: Bundle?
  ): View? {
      binding = DataBindingUtil.inflate<FragmentClassificationContentBinding>(
          layoutInflater,
          R.layout.fragment_classification_content, container, false
      )
      return binding!!.root
  }

  override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
      super.onViewCreated(view, savedInstanceState)
      position = arguments!!.getInt("position")
      initData()
      initView()

  }

  var mContentInfos = ArrayList<ClassificationContentInfo>()
  var contentAdapter: ClassificationContentAdapter? = null
  private fun initData() {
      var contentInfo = ClassificationContentInfo()
      contentInfo.title = "家電"
      contentInfo.imgs = imgs
      mContentInfos.add(contentInfo)
  }

  private fun initView() {

      contentAdapter = ClassificationContentAdapter(activity!!);
      binding!!.rvContent.layoutManager = LinearLayoutManager(activity!!)
      binding!!.rvContent.adapter = contentAdapter
      contentAdapter!!.addItems(mContentInfos)

      binding!!.sfLayout.setOnRefreshLoadMoreListener(object : OnRefreshLoadMoreListener {
          override fun onRefresh(refreshLayout: RefreshLayout) {
              postRefreshLoadMore(0)
          }

          override fun onLoadMore(refreshLayout: RefreshLayout) {
              postRefreshLoadMore(1)
          }
      })
  }
  //延時執(zhí)行刷新 為了保留 頂部和頂部的刷新布局
  fun postRefreshLoadMore(type: Int) {
      binding!!.sfLayout.postDelayed(object : Runnable {
          override fun run() {
              ClassificationObservable.update(ClassificationObservableInfo(type, position))
          }
      }, finisTime)

  }

  fun setData(contentInfos: ArrayList<ClassificationContentInfo>, position: Int) {
      this.position = position
      mContentInfos = contentInfos
      contentAdapter!!.setNewData(mContentInfos)
      ClassificationFinshObservable.update(position)
      finish()
  }

  fun finish() {
      binding!!.sfLayout.finishLoadMore()
      binding!!.sfLayout.finishRefresh()
      goTop()

  }

  //為了兼容一下 sfLayout 的定時問題 做了一下延時
  fun goTop() {
      binding!!.sfLayout.postDelayed(object : Runnable {
          override fun run() {
              binding!!.rvContent.scrollToPosition(0);
              var mLayoutManager = binding!!.rvContent.getLayoutManager() as LinearLayoutManager
              mLayoutManager.scrollToPositionWithOffset(0, 0);
          }
      }, 100)

  }


}

2.2 layout

<?xml version="1.0" encoding="utf-8"?>
<layout>

    <com.scwang.smartrefresh.layout.SmartRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/sf_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:background="@color/color_m_red"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:text="上拉加載上一頁"
                android:textColor="@color/white" />

        </RelativeLayout>

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/rv_content"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="100dp"
            android:background="@color/color_1aad19"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:text="下拉加載下一頁"
                android:textColor="@color/white" />

        </RelativeLayout>


    </com.scwang.smartrefresh.layout.SmartRefreshLayout>

</layout>

3.左側(cè)適配器

package com.wu.third.classification

import android.content.Context
import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.RecyclerView
import com.wu.base.adapter.KtAdapter
import com.wu.base.base.adapter.KtDataBindingViewHolder
import com.wu.third.R
import com.wu.third.databinding.LayoutClassficationTagBinding


/**
* @author wkq
*
* @date 2021年11月18日 14:55
*
*@des  左側(cè)標(biāo)題的Adapter
*
*/

class ClassificationTagAdapter(mContext: Context) : KtAdapter<ClassificationTagInfo>(mContext) {
  //DiffUtil  的異步刷新的工具
  var diff: AsyncListDiffer<ClassificationTagInfo>? = null

  init {
      this.mContext = mContext
      diff = AsyncListDiffer<ClassificationTagInfo>(this, ClassificationTagDiffItemCallBack())
  }


  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
      var binding = DataBindingUtil.inflate<LayoutClassficationTagBinding>(
          LayoutInflater.from(mContext),
          R.layout.layout_classfication_tag, parent, false
      )

      var holder = KtDataBindingViewHolder(binding.root)
      holder.binding = binding
      return holder
  }


  override fun onBindViewHolder(
      holder: RecyclerView.ViewHolder,
      position: Int,
      payloads: MutableList<Any>
  ) {
      // payloads  為null 整條數(shù)據(jù)刷新
      if (payloads.isEmpty() || payloads.size <= 0) {
          super.onBindViewHolder(holder, position, payloads)
      } else {
          //局部更新   不會更新整個item 更新指定的控件
          var bundle = payloads.get(0) as Bundle
          if (bundle != null) {
              var title = bundle.getString("title")
              var isShow = bundle.getBoolean("isShow")
              var holder = holder as KtDataBindingViewHolder
              var binding = holder.binding as LayoutClassficationTagBinding
              if (!TextUtils.isEmpty(title)) {
                  binding.tvTag.text = title
              }
              if (isShow) {
                  binding.rlRoot.setBackgroundColor(mContext.resources.getColor(R.color.color_23d41e))
              } else {
                  binding.rlRoot.setBackgroundColor(mContext.resources.getColor(R.color.white))
              }
          }
      }
  }

  override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
      var bindingHolder = holder as KtDataBindingViewHolder
      var binding = bindingHolder.binding as LayoutClassficationTagBinding
      binding.tvTag.text = getAsyncListItem(position)!!.title
      if (getAsyncListItem(position)!!.isShow) {
          binding.rlRoot.setBackgroundColor(mContext.resources.getColor(R.color.color_23d41e))
      } else {
          binding.rlRoot.setBackgroundColor(mContext.resources.getColor(R.color.white))
      }

      binding.rlRoot.setOnClickListener {
          if (viewClickListener!=null){
              viewClickListener!!.onViewClick( binding.rlRoot,getAsyncListItem(position))
          }
      }

  }

  fun setAsyncListDatas(newList: List<ClassificationTagInfo>) {
      if (diff == null) return
      diff!!.submitList(newList)
  }

  fun getAsyncListItem(position: Int): ClassificationTagInfo {
      return diff!!.currentList.get(position)
  }

  fun getAsyncListItems(): List<ClassificationTagInfo> {
      return diff!!.currentList
  }

  override fun getItemCount(): Int {
      return diff!!.currentList.size
  }


}

3.2 ClassificationTagDiffItemCallBack

package com.wu.third.classification

import android.os.Bundle
import android.text.TextUtils
import androidx.recyclerview.widget.DiffUtil


/**
 * @author wkq
 *
 * @date 2021年09月06日 14:33
 *
 *@des  異步差分對比的監(jiān)聽
 *
 */

class ClassificationTagDiffItemCallBack() :
    DiffUtil.ItemCallback<ClassificationTagInfo>() {

    //是否相同的id
    override fun areItemsTheSame(oldItem: ClassificationTagInfo, newItem: ClassificationTagInfo): Boolean {
        return oldItem.title!!.equals(oldItem.title)

    }
    //是否相同的數(shù)據(jù)
    override fun areContentsTheSame(oldItem: ClassificationTagInfo, newItem: ClassificationTagInfo): Boolean {
        var oldItem = oldItem.toString()
        var newItem = newItem.toString()
        return oldItem.equals(newItem)
    }


    //  局部刷新  返回null 整條數(shù)據(jù)刷新
    override fun getChangePayload(oldItem: ClassificationTagInfo, newItem: ClassificationTagInfo): Any? {
        var bundle = Bundle()
        // onBindViewHolder  實現(xiàn)三個參數(shù)  payloads   第一個數(shù)據(jù)為  封裝的bundle
        if (!TextUtils.equals(oldItem.title, newItem.title)) {
            bundle.putString("title", newItem.title)
        }
        if (oldItem.isShow!=newItem.isShow) {
            bundle.putBoolean("isShow", newItem.isShow)
        }
        return bundle
    }


}

3.3右側(cè)Adapter

package com.wu.third.classification

import android.content.Context
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.wu.base.adapter.KtAdapter
import com.wu.base.base.adapter.KtDataBindingViewHolder
import com.wu.third.R
import com.wu.third.databinding.ItemClassificationContentBinding


/**
 * @author wkq
 *
 * @date 2021年11月18日 15:03
 *
 *@des  右側(cè)內(nèi)容的Adapter
 *
 */

class ClassificationContentAdapter(mContext: Context) :
    KtAdapter<ClassificationContentInfo>(mContext) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        var binding = DataBindingUtil.inflate<ItemClassificationContentBinding>(
            LayoutInflater.from(mContext),
            R.layout.item_classification_content, parent, false
        )
        var holder = KtDataBindingViewHolder(binding.root)
        holder.binding = binding
        return holder
    }
    var binding:ItemClassificationContentBinding?=null
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        var bindingHolder = holder as KtDataBindingViewHolder
         binding = bindingHolder.binding as ItemClassificationContentBinding
        binding!!.tvTitle.text = getItem(position)!!.title
        binding!!.rvImgs.layoutManager = GridLayoutManager(mContext, 4)
        var adapter = ClassificationImgsAdapter(mContext)
        binding!!.rvImgs.adapter = adapter
        adapter.addItems(getItem(position)!!.imgs)
    }

    fun setNewData(newDatas:ArrayList<ClassificationContentInfo>){
        removeAllItems()
        itemList=newDatas
        notifyDataSetChanged()
        binding!!.rvImgs.scrollToPosition(0)
    }


}

新增:自定義LinearLayoutManager左側(cè)分類自動居中邏輯

package com.wu.third.classification;

import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;

import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.LinearSmoothScroller;
import androidx.recyclerview.widget.RecyclerView;

/**
 * @author wkq
 * @date 2021年11月26日 16:57
 * @des
 */


public class CenterLinearLayoutManager extends LinearLayoutManager {

    public CenterLinearLayoutManager(Context context) {
        super(context);
    }

    public CenterLinearLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    public CenterLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
        RecyclerView.SmoothScroller smoothScroller = new CenterSmoothScroller(recyclerView.getContext());
        smoothScroller.setTargetPosition(position);
        startSmoothScroll(smoothScroller);
    }

    private static class CenterSmoothScroller extends LinearSmoothScroller {

        public CenterSmoothScroller(Context context) {
            super(context);
        }

        @Override
        public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) {
            return (boxStart + (boxEnd - boxStart) / 2) - (viewStart + (viewEnd - viewStart) / 2);
        }

        //滾動速度
        @Override
        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
            return 400f / displayMetrics.densityDpi;
        }

    }

}

自動滑動到中間調(diào)用

        //自動滑動到中間
        centerLinearLayoutManager!!.smoothScrollToPosition(binding!!.rvClassify, RecyclerView.State(),position)

總結(jié)

大致邏輯代碼就是這樣,核心實現(xiàn)了 商品分類點擊右側(cè)切換數(shù)據(jù),右側(cè)商品分類滑動切換左側(cè)商品分類的邏輯.商品右側(cè)的分類實現(xiàn)了上拉 和下拉的一部分動畫

資源

1. DiffUti局部刷新的介紹

2. DiffUti局部刷新的介紹(異步刷新)

3. 源碼下載,歡迎Star

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市雨膨,隨后出現(xiàn)的幾起案子审丘,更是在濱河造成了極大的恐慌破花,老刑警劉巖家卖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)犹芹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仁讨,“玉大人,你說我怎么就攤上這事实昨《椿恚” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵荒给,是天一觀的道長丈挟。 經(jīng)常有香客問我,道長志电,這世上最難降的妖魔是什么曙咽? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮挑辆,結(jié)果婚禮上例朱,老公的妹妹穿的比我還像新娘孝情。我一直安慰自己,他們只是感情好洒嗤,可當(dāng)我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布箫荡。 她就那樣靜靜地躺著,像睡著了一般渔隶。 火紅的嫁衣襯著肌膚如雪羔挡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天间唉,我揣著相機(jī)與錄音绞灼,去河邊找鬼。 笑死呈野,一個胖子當(dāng)著我的面吹牛低矮,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播际跪,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼商佛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了姆打?” 一聲冷哼從身側(cè)響起良姆,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎幔戏,沒想到半個月后玛追,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡闲延,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年痊剖,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垒玲。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡陆馁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出合愈,到底是詐尸還是另有隱情叮贩,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布佛析,位于F島的核電站益老,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏寸莫。R本人自食惡果不足惜捺萌,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望膘茎。 院中可真熱鬧桃纯,春花似錦酷誓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至驮配,卻和暖如春娘扩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背壮锻。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工琐旁, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人猜绣。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓灰殴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掰邢。 傳聞我的和親對象是個殘疾皇子牺陶,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容