实现效果:

实现方案介绍

recyclerView实现吸顶效果有2种方案:
方案1: 通过ItemDecoration 并重写对应的getItemOffsets()、onDraw()、onDrawOver()方法
方案2: 通过xml布局,并设置RecyclerView的scrollListener的监听的方式

简单讲下2种方式优缺点:
方案1:
优点:封装性好,使用友好,相关逻辑封装在ItemDecoration中,没有外部逻辑
缺点:扩展性不好,需要通过canvas进行绘制,如果吸顶控件ui比较复杂,绘制起来会很麻烦,并且ui一旦不一致就需要重新写canvas相关代码

方案2:
优点:扩展性比较好,吸顶控件可以通过xml的方式进行编写
缺点:封装性不强,需要对RecyclerView设置ScrollListener

最终方案:基于扩展性的考虑,推荐使用方案2,对方案2封装性不强的问题,可以通过代码来封装的更好一些. 关于方案一后面也会有博客写一下的
方案一与方案而,思路上都是比较上一个的标记与下一个的标记是否相同,不相同则更新吸顶布局的ui

方案2 封装与实现

这部分感觉思路上看不太懂的可以先看封装前的代码,封装前代码复制在下方了

lib封装:

BaseStickView

该类主要是为了规范后续其他人是用的时候,创建的HeaderView或者ItemView必须继承BaseStickView—》参考后面Demo中的StickyHeaderView与StickyItemView便能理解


abstract class BaseStickView : FrameLayout {constructor(context: Context) : super(context)constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle)init {inflate(context, getContentId(), this)}abstract fun onBind(data: Any?)abstract fun getContentId(): Int
}

StickModel

数据模型,对原始数据进行转换,
desTag用于赋值给 itemView.contentDescription,参考 StickHolder中bind方法的代码

class StickModel(var itemData: Any? = null,var headerData: Any? = null,var isHeader: Boolean = false,var desTag: String? = null
)

Holder封装

IStickHolder

interface IStickHolder {fun bind(position: Int, data: Any?)fun getTag(): Int?
}

StickHolder

abstract class StickHolder(view: View) : RecyclerView.ViewHolder(view), IStickHolder {companion object {const val NONE_STICKY_VIEW: Int = 111const val FIRST_STICKY_VIEW: Int = 222const val HAS_STICKY_VIEW: Int = 333}private var tag: Int? = nullvar data: Any? = nulloverride fun bind(position: Int, data: Any?) {if (position == 0) {tag = FIRST_STICKY_VIEW} else {if (data is StickModel) {tag = if (data.isHeader) HAS_STICKY_VIEW else NONE_STICKY_VIEW}}this.data = dataif (data is StickModel) {itemView.contentDescription = data.desTag}}override fun getTag(): Int? {return tag}
}

StickyListLayout

自定义属性attrs.xml文件

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="StickyListLayout"><attr name="stickLayoutId" format="reference|integer"/><attr name="stickHeaderViewId" format="reference|integer"/><attr name="recyclerViewId" format="reference|integer"/></declare-styleable>
</resources>

StickyListLayout代码

class StickyListLayout : FrameLayout {private var headerView: BaseStickView? = nullprivate var recyclerView: RecyclerView? = nullprivate var layoutId: Int = 0private var headerViewId: Int = 0private var recyclerViewId: Int = 0constructor(context: Context) : this(context, null)constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {parseAttributes(attrs)generateView()}private fun parseAttributes(attrs: AttributeSet?) {val ta = context.obtainStyledAttributes(attrs, R.styleable.StickyListLayout)layoutId = ta.getResourceId(R.styleable.StickyListLayout_stickLayoutId, 0)headerViewId = ta.getResourceId(R.styleable.StickyListLayout_stickHeaderViewId, 0)recyclerViewId = ta.getResourceId(R.styleable.StickyListLayout_recyclerViewId, 0)ta.recycle()}private fun generateView() {if (layoutId == 0 || headerViewId == 0 || recyclerViewId == 0) {return}inflate(context, layoutId, this)headerView = findViewById(headerViewId)recyclerView = findViewById(recyclerViewId)initRecyclerView()}private fun initRecyclerView() {val lp = recyclerView?.layoutParamslp?.width = ViewGroup.LayoutParams.MATCH_PARENTlp?.height = ViewGroup.LayoutParams.WRAP_CONTENTrecyclerView?.layoutManager = LinearLayoutManager(context)recyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() {override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {super.onScrolled(recyclerView, dx, dy)headerView ?: returnrefreshStickyHeaderView()translateStickyHeaderView()}})}private fun translateStickyHeaderView() {val underHeaderView = recyclerView?.findChildViewUnder(0f, stickyHeaderView.measuredHeight + 1f)underHeaderView ?: returnval holder = recyclerView?.getChildViewHolder(underHeaderView)if (holder is StickHolder) {when (holder.getTag()) {HAS_STICKY_VIEW -> {val top = underHeaderView.topheaderView!!.translationY = if (top > 0) (top - headerView!!.measuredHeight).toFloat() else 0f}else -> {headerView!!.translationY = 0f}}}}private fun refreshStickyHeaderView() {val view = recyclerView?.findChildViewUnder(0f, 0f)view ?: returnif (view.contentDescription != headerView!!.contentDescription) {val holder = recyclerView?.getChildViewHolder(view)if (holder is StickHolder) {headerView!!.onBind(holder.data)}}}fun bindAdapter(adapter: RecyclerView.Adapter<RecyclerView.ViewHolder>) {recyclerView?.adapter = adapter}fun refreshHeaderView(data: Any) {stickyHeaderView?.onBind(data)}
}

基于封装的使用demo

使用时需要2套Model(HeaderData、ItemData) 2套holder(StickyHeaderHolder、StickyItemHolder) 2套View(StickyHeaderView、StickyItemView),一个Adapter,一个使用Activity

数据

class HeaderData(val iconResId: Int, val title: String)
class ItemData(val iconResId: Int, val des: String)

HeaderView与ItemView 二者都需要继承BaseStickView

headerView

class StickyHeaderView : BaseStickView {constructor(context: Context) : super(context)constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle)private val iv: ImageView by lazy {this.findViewById<ImageView>(R.id.icon)}private val tv: TextView by lazy {this.findViewById<TextView>(R.id.tv)}override fun getContentId(): Int {return R.layout.demo_header}override fun onBind(data: Any?) {if (data !is StickModel) {return}val headerData = data.headerDataheaderData ?: returnif (headerData is HeaderData) {iv.setImageResource(headerData.iconResId)tv.text = headerData.title}}
}
itemView
class StickyItemView : BaseStickView {constructor(context: Context) : super(context)constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)constructor(context: Context, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle)private val iv: ImageView by lazy {this.findViewById<ImageView>(R.id.icon)}private val tv: TextView by lazy {this.findViewById<TextView>(R.id.tv)}override fun getContentId(): Int {return R.layout.demo_item}override fun onBind(data: Any?) {if (data !is StickModel) {return}val itemData = data.itemDataitemData ?: returnif (itemData is ItemData) {iv.setImageResource(itemData.iconResId)tv.text = itemData.des}}}
xml文件

demo_header.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#EFFAE7"android:gravity="center_vertical"
><ImageViewandroid:id="@+id/icon"android:layout_width="40dp"android:layout_height="40dp"android:layout_marginLeft="@dimen/dimens_16dp"android:layout_marginRight="@dimen/dimens_16dp"/><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="50dp"android:gravity="center"android:layout_marginLeft="@dimen/dimens_10dp"android:layout_toRightOf="@+id/icon"android:text="吸顶文本1"/>
</RelativeLayout>

demo_item

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content"><ImageViewandroid:id="@+id/icon"android:layout_width="30dp"android:layout_height="30dp"android:layout_marginLeft="@dimen/dimens_16dp"android:layout_marginRight="@dimen/dimens_16dp"/><TextViewandroid:id="@+id/tv"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center"android:textColor="@color/colorPrimaryDark"android:layout_toRightOf="@+id/icon"android:layout_centerVertical="true"/><Viewandroid:layout_width="match_parent"android:layout_height="2dp"android:layout_alignParentBottom="true"android:background="#ffffff"android:layout_below="@+id/icon"/>
</RelativeLayout>

DemoAdapter

class DemoAdapter(var data: MutableList<StickModel>? = null) : RecyclerView.Adapter<StickHolder>() {companion object {const val ITEM_TYPE_HEADER: Int = 0x11const val ITEM_TYPE_ITEM: Int = 0x22}override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StickHolder {return when (viewType) {ITEM_TYPE_HEADER -> StickHeaderHolder(parent)else -> StickItemHolder(parent)}}override fun getItemViewType(position: Int): Int {return if (data?.get(position)?.isHeader == true) ITEM_TYPE_HEADER else ITEM_TYPE_ITEM}override fun getItemCount(): Int {return data?.size ?: 0}override fun onBindViewHolder(holder: StickHolder, position: Int) {data?.get(position) ?: returnholder.bind(position, data?.get(position))}
}

DemoActivity

xml

activity_demo.xml
StickyListLayout必须配置对应的app:stickLayoutId、app:stickLayoutId、app:recyclerViewId
这几项

<?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"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><com.example.stickyheaderlistdemo.sample.view.StickyListLayoutandroid:id="@+id/stickyListLayout"android:layout_width="match_parent"android:layout_height="match_parent"app:stickLayoutId="@layout/sticky_layout"app:stickLayoutId="@id/stickyHeaderView"app:recyclerViewId="@id/recyclerView"/>
</LinearLayout>

sticky_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayoutxmlns: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:background="@color/colorAccent"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_height="match_parent"android:layout_width="wrap_content"/><com.example.stickyheaderlistdemo.sample.demo.view.StickyHeaderViewandroid:id="@+id/stickyHeaderView"android:layout_width="match_parent"android:layout_height="wrap_content"/></FrameLayout>

demoActivity

class DemoActivity : AppCompatActivity() {private var stickModels: MutableList<StickModel> = mutableListOf()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_demo)initData()stickyListLayout.bindAdapter(DemoAdapter(stickModels) as RecyclerView.Adapter<RecyclerView.ViewHolder>)stickyListLayout.refreshHeaderView(stickModels!![0])}private fun initData() {stickModels.clear()for (index in 0..99) {if (index < 15) {if (index == 0) {stickModels?.add(StickModel(null, HeaderData(R.drawable.surprise, "这组是惊喜的表情"), true, "surprise"))}stickModels?.add(StickModel(ItemData(R.drawable.surprise, "第${index}个惊喜的表情 hhhhhhhhhhhhhh"),null,false,"surprise"))} else if (index < 25) {if (index == 15) {stickModels?.add(StickModel(null, HeaderData(R.drawable.angry, "这组是生气的表情"), true, "angry"))}stickModels?.add(StickModel(ItemData(R.drawable.angry, "第${index}个生气的表情"), null, false, "angry"))} else if (index < 35) {if (index == 25) {stickModels?.add(StickModel(null, HeaderData(R.drawable.sad, "这组是悲伤的表情"), true, "sad"))}stickModels?.add(StickModel(ItemData(R.drawable.sad, "第${index}个悲伤的表情"), null, false, "sad"))} else {if (index == 35) {stickModels?.add(StickModel(null, HeaderData(R.drawable.happy, "这一组是高兴的表情"), true, "happy"))}stickModels?.add(StickModel(ItemData(R.drawable.happy, "第${index}个高兴的表情"), null, false, "happy"))}}}
}

封装前的代码

封装前的代码是参考的其他人的,这里直接贴出来

Activity相关

xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayoutxmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/cy"android:layout_width="match_parent"android:layout_height="match_parent"tools:context=".MainActivity"><androidx.recyclerview.widget.RecyclerViewandroid:id="@+id/recyclerView"android:layout_height="match_parent"android:layout_width="wrap_content"/><TextViewandroid:id="@+id/tv_sticky_header_view"android:layout_width="match_parent"android:layout_height="50dp"android:background="#EFFAE7"android:gravity="center"android:text="吸顶文本1"/></FrameLayout>

SampleActivity

class SampleActivity : AppCompatActivity() {private var recyclerView: RecyclerView? = nullprivate var tvStickyHeaderView: TextView? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_sample_1)initView()initListener()}private fun initListener() {recyclerView?.addOnScrollListener(object : OnScrollListener() {override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {super.onScrolled(recyclerView, dx, dy)tvStickyHeaderView ?: returnupdateStickyHeader(recyclerView)val transInfoView = recyclerView.findChildViewUnder(0f, (tvStickyHeaderView?.height ?: 0 + 1).toFloat())transInfoView?.tag?.let {val transViewStatus: Int =it as Intval top = transInfoView.topwhen (transViewStatus) {HAS_STICKY_VIEW -> {tvStickyHeaderView?.translationY = if (top > 0) (top - tvStickyHeaderView!!.measuredHeight).toFloat() else 0f}else -> {tvStickyHeaderView?.translationY = 0f}}}}})}private fun updateStickyHeader(recyclerView: RecyclerView) {val stickView = recyclerView.findChildViewUnder(0f, 0f)stickView?.contentDescription ?: returnif (stickView.contentDescription != tvStickyHeaderView?.text) {tvStickyHeaderView?.text = stickView.contentDescription}}private fun initView() {recyclerView = findViewById(R.id.recyclerView)tvStickyHeaderView = findViewById(R.id.tv_sticky_header_view)recyclerView?.layoutManager = LinearLayoutManager(this)recyclerView?.adapter = SampleAdapter2(getData())}private fun getData(): MutableList<StickyBean>? {val stickyExampleModels = mutableListOf<StickyBean>()for (index in 0..99) {if (index < 15) {if (index == 0) {stickyExampleModels.add(StickyBean("吸顶文本1", "name$index", "gender$index", true))}stickyExampleModels.add(StickyBean("吸顶文本1", "name$index", "gender$index", false))} else if (index < 25) {if (index == 15) {stickyExampleModels.add(StickyBean("吸顶文本2", "name$index", "gender$index", true))}stickyExampleModels.add(StickyBean("吸顶文本2", "name$index", "gender$index", false))} else if (index < 35) {if (index == 25) {stickyExampleModels.add(StickyBean("吸顶文本3", "name$index", "gender$index", true))}stickyExampleModels.add(StickyBean("吸顶文本3", "name$index", "gender$index", false))} else {if (index == 35) {stickyExampleModels.add(StickyBean("吸顶文本4", "name$index", "gender$index", true))}stickyExampleModels.add(StickyBean("吸顶文本4", "name$index", "gender$index", false))}}return stickyExampleModels}
}

bean

public class StickyBean {public String name;public String autor;public String sticky;public Boolean isHeader;public StickyBean(String sticky,String name,String autor,Boolean isHeader) {this.sticky = sticky;this.name = name;this.autor = autor;this.isHeader = isHeader;}
}

Adapter

class SampleAdapter2(var data: MutableList<StickyBean>? = mutableListOf()) :RecyclerView.Adapter<Holder>() {override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {return Holder(LayoutInflater.from(parent.context).inflate(R.layout.sample_1_item, parent, false))}override fun getItemCount(): Int {return data?.size ?: 0}override fun onBindViewHolder(holder: Holder, position: Int) {holder.bindData(data?.get(position), position, data)}}class Holder(view: View) : RecyclerView.ViewHolder(view) {var tvStickyHeader: TextView? = nullvar rlContentWrapper: RelativeLayout? = nullvar tvName: TextView? = nullvar tvGender: TextView? = nullcompanion object {const val NONE_STICKY_VIEW: Int = 111const val FIRST_STICKY_VIEW: Int = 222const val HAS_STICKY_VIEW: Int = 333}init {tvStickyHeader = view.findViewById(R.id.tv_sticky_header_view)rlContentWrapper = view.findViewById(R.id.rl_content_wrapper)tvName = view.findViewById(R.id.name)tvGender = view.findViewById(R.id.auto)}fun bindData(stickyBean: StickyBean?, position: Int, data: MutableList<StickyBean>?) {stickyBean ?: returntvName?.text = stickyBean.nametvGender?.text = stickyBean.autorif (position == 0) {tvStickyHeader?.visibility = View.VISIBLEtvStickyHeader?.text = stickyBean.stickyitemView.tag = FIRST_STICKY_VIEW} else {if (stickyBean.isHeader) {tvStickyHeader?.visibility = View.VISIBLEtvStickyHeader?.text = stickyBean.stickyitemView.tag = HAS_STICKY_VIEW} else {tvStickyHeader?.visibility = View.GONEtvStickyHeader?.text = stickyBean.stickyitemView.tag = NONE_STICKY_VIEW}}itemView.contentDescription = stickyBean.sticky}
}

xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"><RelativeLayoutandroid:id="@+id/rl_content_wrapper"android:layout_width="match_parent"android:layout_height="30dp"android:layout_marginLeft="5dp"android:layout_marginRight="5dp"><TextViewandroid:id="@+id/name"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerVertical="true" /><TextViewandroid:id="@+id/auto"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentRight="true"android:layout_centerVertical="true" /><Viewandroid:layout_width="match_parent"android:layout_height="1dp"android:layout_alignParentBottom="true"android:background="#ffffff" /></RelativeLayout><TextViewandroid:id="@+id/tv_sticky_header_view"android:layout_width="match_parent"android:layout_height="50dp"android:background="#EFFAE7"android:gravity="center"android:text="吸顶文本1" /></FrameLayout>

Android RecyclerView 吸顶效果(一)相关推荐

  1. Android - 吸顶效果 布局篇

    调研了一下微博和豆瓣等大体量的APP,发现内容详情页的评论吸顶效果非常常见. 以截图自豆瓣的效果为例,当上划至内容部分消失时,滑动中的回复条会置顶,并保持在位置不动. 笔者通过实践,记录下目前发现的最 ...

  2. android滑动自动吸顶效果,安卓当下最流行的吸顶效果的实现(上)

    原标题:安卓当下最流行的吸顶效果的实现(上) 开始逐渐领略到ItemDecoration的美~ 今天让我 使用 ItemDecoration 来完成 可推动的悬浮导航栏的效果,最终实现的效果如下图: ...

  3. 仿微博发现页吸顶效果

    整个首页布局采用 CoordinatorLayout 布局,实现正常的吸顶效果.然后监听 AppBarLayout 的展开与折叠.我直接上代码吧. 首页外部布局: <androidx.coord ...

  4. 最贴近京东首页体验的嵌套滑动吸顶效果

    吸顶效果是各家 App 或多或少都会用的一个交互,这种交互也常见于 PC.H5,可以说是一种通用性很强的前端交互体验,相比较来说京东首页的嵌套滑动吸顶效果是各个类似效果中体验比较好的一个,因为在嵌套布 ...

  5. 自定义tab吸顶效果一

    PS:问题:什么是吸顶,吸顶有什么作用,吸顶怎么使用? 在很多app商城中,介绍软件的时候就会使用吸顶效果, 吸顶有很多作用,一个最简单粗暴的作用就是,让用户知道此刻在浏览哪个模块,并可以选择另外的模 ...

  6. Unity实现ScrollView并包含吸顶效果

    1 前言 想在Unity做一个类似android/ios的界面,可上下滑动,可吸顶,该咋做呢? 本文就来做个demo. 先上效果: Scroll View是一个2D UI控件,新建好了,会自动包含子节 ...

  7. 【使用篇】WebView 实现嵌套滑动,丝滑般实现吸顶效果,完美兼容 X5 webview

    本文首发我的公众号徐公,收录于 Github·AndroidGuide,这里有 Android 进阶成长知识体系, 希望我们能够一起学习进步,关注公众号徐公,5 年中大厂程序员,一起建立核心竞争力 背 ...

  8. 30秒实现Vue吸顶效果

    酱酱,好久不见鸭! 前言:吸顶效果图: 1.滚动前: image.png 2.滚动中: image.png 3.滚动超过后: image.png 直观效果可参pc端微博左侧的信息栏 第一步:html ...

  9. vue音乐项目歌手页面滚动、吸顶效果

    总结singer页面: 1.api中去获取 ['热',A-Z] 以及根据['热',A-Z]获取的所有歌手的数据 2.渲染数据 2.1 渲染左边 字母title ['热',A-Z] + 该字母开头的歌手 ...

最新文章

  1. oracle查看临时表空间文件,Oracle-临时表空间
  2. 电脑教程从入门到精通_HALCON机器视觉软件零基础入门学习到精通实用教学视频教程...
  3. 12.Linux之输入子系统分析(详解)
  4. GIT-Linux(CentOS7)系统部署git服务器
  5. Asp.Net Boilerplate微服务实战(一)概述
  6. 如何快速的入门Docker并且实现部署
  7. 操作系统知识--线程
  8. 小布语音下载安装_一加语音助手小布体验:还有很长的路要走
  9. 允许外部访问Windows本机的指定端口
  10. cdlinux 无线网密码破解
  11. matlab2010激活问题
  12. 2020算法设计与分析 官方考前模拟卷 参考答案
  13. 00 SQL课程简介
  14. Unity中的万向节死锁
  15. Linux安装 Oracle 19C 实操
  16. android 获取刘海高度,Android各厂商的刘海屏高度获取工具类
  17. 【100个 Unity实用技能】☀️ | Unity中 检查当前设备网络状态 的几种方法整理
  18. 数据库设计中String类型的数据库类型选择
  19. 由浅入深玩转华为WLAN—19 漫游系列(6)不同AC之间AP漫游的概述以及二三层漫游处理过程
  20. 在8×8格的国际象棋上摆放8个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,用Python编写程序,问有多少种摆法?并列举出所有摆法。

热门文章

  1. AI赋能航空,从安全保障到用户体验变革即将发生
  2. 中秋节2007中秋节中秋祝福
  3. Python颜色光谱单色效果图
  4. Transformer-XL全解读
  5. [评论随笔]明朝后期的中缅战争——此役之后关系中国西南版图
  6. 6-16 模型融合 WBF
  7. 《数据仓库与数据挖掘》期末复习总结
  8. BOJ 4228 哈哈哈
  9. 一起Talk Android吧(第四百五十九回:SeekBar补充用法二)
  10. 过期域名有价值吗?抢注过期域名的几大注意事项