Paging3 (一)  入门

前言:

官方分页工具,  确实香.   但数据源不开放, 无法随意增删改操作;  只能借助 Room;  但列表数据不一定都要用 Room吧;

如果偏查询的分页数据用 Paging3 ;  其他一概用 老Adapter;  这倒也算个方案. [苦笑]

目录:

  1. 简单使用  -  数据源,Viewmodel,Adapter 等
  2. LoadResult  -  Error, Page.  Error 用法等
  3. PagingConfig
  4. 监听列表加载状态
  5. LoadStateAdapter  -  loading, 加载失败, 没有更多等
  6. Map  -  数据预处理

官方 Pagings 优势: 

  • 分页数据的内存中缓存。该功能可确保您的应用在处理分页数据时高效利用系统资源。
  • 内置的请求重复信息删除功能,可确保您的应用高效利用网络带宽和系统资源。
  • 可配置的 RecyclerView 适配器,会在用户滚动到已加载数据的末尾时自动请求数据。
  • 对 Kotlin 协程和 Flow 以及 LiveData 和 RxJava 的一流支持。
  • 内置对错误处理功能的支持,包括刷新和重试功能。

导包:

dependencies {
val paging_version = "3.0.0" //唯一必导包
implementation("androidx.paging:paging-runtime:$paging_version") // 测试用
testImplementation("androidx.paging:paging-common:$paging_version") // optional - RxJava2 support
implementation("androidx.paging:paging-rxjava2:$paging_version") // optional - RxJava3 support
implementation("androidx.paging:paging-rxjava3:$paging_version") // 适配 Guava 库 - 高效java扩展库
implementation("androidx.paging:paging-guava:$paging_version") // 适配 Jetpack Compose - 代码构建View; 干掉 layout
implementation("androidx.paging:paging-compose:1.0.0-alpha09")
}

1. 简单使用:

1.1 数据源  PagingSource 

自定义数据源, 继承 PagingSource

它有两个泛型参数,  1. 页码key,  没有特殊需求的话一般就是 Int 类型;  2.集合实体类型

重写两个方法:  1.load()  加载数据的方法;   2.getRefreshKey  初始加载的页码;  暂且返回 1 或 null

LoadResult.Page 后面再讲;

class DynamicDataSource: PagingSource<Int, DynamicTwo>() {

    //模拟最大页码
private var maxPage = 2 //模拟数据
private fun fetchItems(startPosition: Int, pageSize: Int): MutableList<DynamicTwo> {
Log.d("ppppppppppppppppppppp", "startPosition=${startPosition};;;pageSize=${pageSize}")
val list: MutableList<DynamicTwo> = ArrayList()
for (i in startPosition until startPosition + pageSize) {
val concert = DynamicTwo()
concert.title = "我是标题${i}"
concert.newsInfo = "我是内容${i}"
concert.nickName = "小王${i}"
list.add(concert)
}
return list
} override fun getRefreshKey(state: PagingState<Int, DynamicTwo>): Int? = null override suspend fun load(params: LoadParams<Int>): LoadResult<Int, DynamicTwo> {
val nextPageNumber = params.key ?: 1
val size = params.loadSize
Log.d("ppppppppppppppppppppp", "nextPageNumber=${nextPageNumber};;;size=${size}")
val response = fetchItems((nextPageNumber-1) * size, size) return LoadResult.Page(
data = response,
prevKey = null, // Only paging forward. 只向后加载就给 null
//nextKey 下一页页码; 尾页给 null; 否则当前页码加1
nextKey = if(nextPageNumber >= maxPage) null else (nextPageNumber + 1)
)
}
}

1.2 ViewModel

代码比较简单.  内容我们一会再讲

class DynamicPagingModel(application: Application) : AndroidViewModel(application) {
val flow = Pager(
//配置
PagingConfig(pageSize = 10, prefetchDistance = 2,initialLoadSize = 10)
) {
//我们自定义的数据源
DynamicDataSource()
}.flow
.cachedIn(viewModelScope)
}

1.3 前台使用: 

初始化 Adapter 及 RecycleView

mViewModel?.flow?.collectLatest  绑定监听,  然后通过 submitData() 刷新列表;

mAdapter = SimplePagingAdapter(R.layout.item_dynamic_img_two, null)

mDataBind.rvRecycle.let {
it.layoutManager = LinearLayoutManager(mActivity)
it.adapter = mAdapter
} //Activity 用 lifecycleScope
//Fragments 用 viewLifecycleOwner.lifecycleScope
viewLifecycleOwner.lifecycleScope.launchWhenCreated {
mViewModel?.flow?.collectLatest {
mAdapter.submitData(it)
}
}

1.4 Adapter

必须继承  paging 的 PagingDataAdapter

DiffCallback() 或 handler  NewViewHolder 不了解的可以看我的 ListAdapter 封装系列

open class SimplePagingAdapter(
private val layout: Int,
protected val handler: BaseHandler? = null
) :
PagingDataAdapter<DynamicTwo, RecyclerView.ViewHolder>(DiffCallback()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return NewViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context), layout, parent, false
), handler
)
} override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if(holder is NewViewHolder){
holder.bind(getItem(position))
}
}
}

over  简单的分页模拟数据已完成;  

2. LoadResult

它是一个密封类;   它表示加载操作的结果;

2.1 LoadResult.Error

表示加载失败;  需提供 Throwable 对象.

public data class Error<Key : Any, Value : Any>(
val throwable: Throwable
) : LoadResult<Key, Value>()

可用于:

  • 异常时返回,  HTTP, IO, 数据解析等异常;
  • 服务器错误码响应
  • 没有更多数据

2.1 LoadResult.Page

表示加载成功;

参数:

data 数据集合;

prevKey 前页页码 key;   //向下一页加载 给null

nextKey 后页页码 key;    //向上一页加载 给null

public data class Page<Key : Any, Value : Any> constructor(
/**
* Loaded data
*/
val data: List<Value>,
/**
* [Key] for previous page if more data can be loaded in that direction, `null`
* otherwise.
*/
val prevKey: Key?,
/**
* [Key] for next page if more data can be loaded in that direction, `null` otherwise.
*/
val nextKey: Key?,
/**
* Optional count of items before the loaded data.
*/
@IntRange(from = COUNT_UNDEFINED.toLong())
val itemsBefore: Int = COUNT_UNDEFINED,
/**
* Optional count of items after the loaded data.
*/
@IntRange(from = COUNT_UNDEFINED.toLong())
val itemsAfter: Int = COUNT_UNDEFINED
) : LoadResult<Key, Value>() {

3.PagingConfig

分页配置

参数:

pageSize:  每页容量

prefetchDistance:  当RecycleView 滑动到底部时, 会自动加载下一页.   如果能提前预加载, 可以省去部分等待加载的时间.

        prefetchDistance 就是距离底部提前加载的距离.  默认 = pageSize;   = 0 时将不会加载更多

enablePlaceholders:  允许使用占位符.  想了解的点这里

initialLoadSize: 初始加载数量,  默认 = pageSize * 3

maxSize:   似乎意义没有那么简单.  还没看源码,不清楚;  不能 < pageSize + prefetchDistance * 2

jumpThreshold: 某阈值!  好吧我摊牌了, 我不知道. [奸笑]

4.监听加载状态:  

LoadState:  表示加载状态密封类;

LoadState.NotLoading:  加载完毕,  并且界面也已相应更新

LoadState.Error:  加载失败.

LoadState.Loading:  正在加载..

lifecycleScope.launch {
mAdapter.loadStateFlow.collectLatest { loadStates ->
when(loadStates.refresh){
is LoadState.Loading -> {
Log.d("pppppppppppppp", "加载中")
}
is LoadState.Error -> {
Log.d("pppppppppppppp", "加载失败")
}
is LoadState.NotLoading -> {
Log.d("pppppppppppppp", "完事了")
}
else -> {
Log.d("pppppppppppppp", "这是啥啊")
}
}
} //或者:
mAdapter.addLoadStateListener { ... }
}

5. 状态适配器  LoadStateAdapter

用于直接在显示的分页数据列表中呈现加载状态。 例如:  尾部显示 正在加载, 加载失败, 没有更多等;

5.1 自定义 MyLoadStateAdapter  继承 LoadStateAdapter

重写  onCreateViewHolder,  onBindViewHolder

retry:  如果加载失败, 想要重试,  则提供该高阶函数参数;  否则不需要它

class MyLoadStateAdapter(
/**
* 当下一页加载失败时, 继续尝试加载下一页;
*/
private val retry: () -> Unit
) : LoadStateAdapter<LoadStateViewHolder>() { override fun onCreateViewHolder(
parent: ViewGroup,
loadState: LoadState
) = LoadStateViewHolder(parent, retry) override fun onBindViewHolder(
holder: LoadStateViewHolder,
loadState: LoadState
) = holder.bind(loadState)
}

5.2 自定义 LoadStateViewHolder

功能: 

  • 加载中 显示 Loading;
  • 加载失败  显示 错误信息.    包括 http, IO 异常,  后台给的错误 msg 等;
  • 没有更多
class LoadStateViewHolder (
parent: ViewGroup,
retry: () -> Unit
) : RecyclerView.ViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.view_loading_more, parent, false)
) {
private val binding = ViewLoadingMoreBinding.bind(itemView) init {
//当点击重试按钮时, 调用 PagingDataAdapter 的 retry() 重新尝试加载
binding.btnLoadingRetry.setOnClickListener {
retry()
}
} fun bind(loadState: LoadState) {
// 当加载失败时.
if(loadState is LoadState.Error){
// 将没有更多封装成 NoMoreException; 此时显示没有更多 View
if(loadState.error is NoMoreException){
hideNoMoreUi(false) //显示 没有更多 View
hideErrUi(true) //隐藏 失败 View
}else{
hideNoMoreUi(true)
hideErrUi(false, loadState.error.message) //显示失败 View时, 填充错误 msg
}
}else{
hideNoMoreUi(true)
hideErrUi(true)
} //加载中..
binding.pbLoadingBar.visibility = if(loadState is LoadState.Loading){
View.VISIBLE
}else{
View.GONE
}
} /**
* 隐藏没有更多View;
*/
private fun hideNoMoreUi(hide: Boolean){
if(hide){
binding.tvLoadingHint.visibility = View.GONE
}else{
binding.tvLoadingHint.visibility = View.VISIBLE
}
} /**
* 隐藏 加载失败View;
*/
private fun hideErrUi(hide: Boolean, msg: String? = null){
if(hide){
binding.tvLoadingError.visibility = View.GONE
binding.btnLoadingRetry.visibility = View.GONE
}else{
binding.tvLoadingError.text = msg
binding.tvLoadingError.visibility = View.VISIBLE
binding.btnLoadingRetry.visibility = View.VISIBLE
}
}
}

顺便补一下  NoMoreException;  用法? 在下面 PagingSource 喽.

class NoMoreException: RuntimeException()

5.3 layout  view_loading_more.xml

包含:   TextView: 没有更多;    ProgressBar: 加载中;   TextView: 错误信息;   Button: 重试按钮

<layout>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:paddingHorizontal="16dp"
android:layout_width="match_parent"
android:layout_height="54dp">
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="#e5e5e5"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/tv_loading_hint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="#798080"
android:text="已经到底了"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<ProgressBar
android:id="@+id/pb_loading_bar"
android:layout_width="32dp"
android:layout_height="32dp"
android:visibility="gone"
android:indeterminateTint="#7671F8"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
<TextView
android:id="@+id/tv_loading_error"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/shape_red"
android:text="错误信息"
android:layout_marginEnd="8dp"
android:maxLines="2"
android:ellipsize="end"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/btn_loading_retry"
app:layout_constraintStart_toStartOf="parent"/>
<Button
android:id="@+id/btn_loading_retry"
android:layout_width="60dp"
android:layout_height="38dp"
android:textColor="@color/white"
android:text="重试"
android:visibility="gone"
android:background="@drawable/shape_blue_7671f8_r8"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

5.4 PagingSource 需要根据情况  返回不同的  LoadResult

代码如下,  直接看注释就可以了;   

class DynamicDataSource: PagingSource<Int, DynamicTwo>() {

    private var maxPage = 1

    override fun getRefreshKey(state: PagingState<Int, DynamicTwo>): Int? = null

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, DynamicTwo> {
try {
val nextPageNumber = params.key ?: 1 //超过页码时, 返回没有更多状态 NoMoreException
if(nextPageNumber > maxPage){
return LoadResult.Error(NoMoreException())
} //这是 Retrofit 网络请求
val map = mapOf("page" to nextPageNumber, "pageSize" to params.loadSize)
val param = ApiManager.INSTANCE.getJsonBody(map)
val response = ApiManager.INSTANCE.mApi.getDynamicList(param) //后台 响应错误码时; 用 RuntimeException 返回错误信息
if(response.code != 200){
return LoadResult.Error(RuntimeException(response.msg))
} //解析响应数据
val jo = response.data
val list = jo?.getAsJsonArray("newsList")?.toString()?.toBeanList<DynamicTwo>() ?: mutableListOf()
maxPage = jo?.get("totalPage").toString().toInt() //返回正常数据
return LoadResult.Page(
data = list,
prevKey = null, // Only paging forward. 只向后加载就给null
// nextKey 下一页页码; 尾页给 null; 否则当前页码加1
nextKey = nextPageNumber + 1
)
} catch (e: IOException) {
// IOException for network failures.
return LoadResult.Error(e)
} catch (e: HttpException) {
// HttpException for any non-2xx HTTP status codes.
return LoadResult.Error(e)
} catch (e: Exception) {
// IOException for network failures.
return LoadResult.Error(e)
}
}
}

代码中 请求参数只给了 page 和 pageSize;  其他参数怎么给?  

  1. DynamicDataSource 的构造方法传入;
  2. 动态参数怎么办?  写回调, 从ViewModel 中组装请求数据
  3. 麻烦怎么办?  创建 BaseDataSource.  将相似代码封装.  请求参数通过高阶函数从ViewModel组装;

5.5 前台使用: 

首先正常初始化 Adapter,  RecycleView,  并调用  mViewModel?.flow?.collectLatest

其次  RecycleView 的 adaper 不要给 主数据Adapter;  而是给 withLoadStateFooter() 返回的 ConcatAdapter

val stateAdapter = mAdapter.withLoadStateFooter(MyLoadStateAdapter(mAdapter::retry))
mDataBind.rvRecycle.let {
it.layoutManager = LinearLayoutManager(mActivity)
// **** 这里不要给 mAdapter(主数据 Adapter); 而是给 stateAdapter ***
it.adapter = stateAdapter
}

PagingDataAdapter 的 withLoadStateFooter 方法会返回一个新的 ConcatAdapter 对象; 请将这个 ConcatAdapter 设置给 RecycleView
withLoadStateFooter 的参数 就是我们自定义的 MyLoadStateAdapter;  retry -> mAdapter.retry()

5.6 看一下  LoadStateAdapter 的源码;  

可以发现,  这是个单条目 Adapter.   

并且  只有当  LoadState.Loading, LoadState.Error 时才会出现;   当然也可以重写  displayLoadStateAsItem(), 让它所有状态都出现;

当 列表状态变化时,  会设置 loadState 参数;  动态增删改 Item;

abstract class LoadStateAdapter<VH : RecyclerView.ViewHolder> : RecyclerView.Adapter<VH>() {

    var loadState: LoadState = LoadState.NotLoading(endOfPaginationReached = false)
set(loadState) {
if (field != loadState) {
val oldItem = displayLoadStateAsItem(field)
val newItem = displayLoadStateAsItem(loadState) if (oldItem && !newItem) {
notifyItemRemoved(0)
} else if (newItem && !oldItem) {
notifyItemInserted(0)
} else if (oldItem && newItem) {
notifyItemChanged(0)
}
field = loadState
}
} final override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
return onCreateViewHolder(parent, loadState)
} final override fun onBindViewHolder(holder: VH, position: Int) {
onBindViewHolder(holder, loadState)
} final override fun getItemViewType(position: Int): Int = getStateViewType(loadState)

  //条目数量, final 不可重写;
final override fun getItemCount(): Int = if (displayLoadStateAsItem(loadState)) 1 else 0 abstract fun onCreateViewHolder(parent: ViewGroup, loadState: LoadState): VH abstract fun onBindViewHolder(holder: VH, loadState: LoadState) open fun getStateViewType(loadState: LoadState): Int = 0
  
  //只有当 Loading, Error 时, 才显示
open fun displayLoadStateAsItem(loadState: LoadState): Boolean {
return loadState is LoadState.Loading || loadState is LoadState.Error
}
}

5.7 LoadStateAdapter  改建头尾

如果我们把它强行改造成 Header footer: 

  1. 重写 displayLoadStateAsItem() 不管什么状态, 都返回true
  2. loadState 不能重写,  所以 notifyItemChanged(0) 必被调用;
  3. 暴力一点, 直接重写 notifyItemChanged() 让它什么都不做?   好吧  它也是 final, 不能重写
  4. 既然要调刷新, 那就调吧 [破涕为笑];  那怎么办 尽量少执行无用代码呗,   那就 onBindViewHolder() 啥也不干;
  5. 头尾由前端控制,  Adapter 只需要把这个 固定View显示就 ok 了
  6. 如果能阻止 notifyItemChanged(0) 那就更好了.  聪明的你有没有办法呢. [666]

最终 Adapter: 

class EndViewAdapter(val v: View) : LoadStateAdapter<EndHolder>() {

    override fun onCreateViewHolder(
parent: ViewGroup,
loadState: LoadState
) = EndHolder(v) override fun onBindViewHolder(holder: EndHolder, loadState: LoadState){
//啥也不干
} override fun displayLoadStateAsItem(loadState: LoadState) = true
} class EndHolder(itemView: View) : RecyclerView.ViewHolder(itemView)

好吧,  一运行, 崩了 [捂脸];   called attach on a child which is not detached

怎么办, 取消 RecycleView 的刷新闪烁动画:

(mDataBind.rvRecycle.itemAnimator as SimpleItemAnimator).supportsChangeAnimations = false;

整个 RecycleView 的条目刷新动画都没了,  这不是个事啊!  但博主已经没办法了 [捂脸]

没办法了怎么办? 不用 Header 了?   当然不是,  我们只是不用 LoadStateAdapter 做头尾了;  我们用 ConcatAdapter 做头尾;

就是在 withLoadState...  之后,  再自己组装  ConcatAdapter

6. MAP:  数据转换;  有的时候, 我们需要对响应数据 进行预先处理; 

例如: 根据条件,预先改变实体内容;

val flow: Flow<PagingData<DynamicTwo>> = Pager(
PagingConfig(pageSize = 10, prefetchDistance = 2,initialLoadSize = 10)
) {
DynamicDataSource()
}.flow
.cachedIn(viewModelScope)
.map {
it.map { entity ->
// 这里根据条件, 预先处理数据
if(entity.isLike == 1){
entity.nickName = "变变变, 我是百变小魔女"
}else{
entity.nickName = "呜哈哈哈"
}
entity
}
}

例如:  组合实体; 根据条件产生不同实体;

val flow: Flow<PagingData<GroupEntity>> = Pager(
PagingConfig(pageSize = 10, prefetchDistance = 2,initialLoadSize = 10)
) {
DynamicDataSource()
}.flow
.cachedIn(viewModelScope)
.map {
it.map { entity ->
// 这里根据条件, 预先处理数据
if(entity.isLike == 1){
GroupEntity.DynamicTwoItem(entity)
}else{
GroupEntity.DynamicItem(DynamicEntity())
}
}
} sealed class GroupEntity{
class DynamicTwoItem (val entity: DynamicTwo): GroupEntity()
class DynamicItem (val entity: DynamicEntity): GroupEntity()
}

又例如: 插入实体分隔符等

Over

回到顶部

孟老板 Paging3 (一) 入门的更多相关文章

  1. 孟老板 Paging3 (二) 结合Room

    BaseAdapter系列 ListAdapter系列 Paging3 (一) 入门 Paging3 (二) 结合 Room Paging3 (二)  结合Room Paging 数据源不开放, 无法 ...

  2. 孟老板 ListAdapter封装, 告别Adapter代码 (三)

    BaseAdapter系列 ListAdapter封装, 告别Adapter代码 (一) ListAdapter封装, 告别Adapter代码 (二) ListAdapter封装, 告别Adapter ...

  3. 孟老板 ListAdapter封装, 告别Adapter代码 (四)

    BaseAdapter系列 ListAdapter封装, 告别Adapter代码 (一) ListAdapter封装, 告别Adapter代码 (二) ListAdapter封装, 告别Adapter ...

  4. 孟老板 BaseAdapter封装(五) ListAdapter

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  5. 孟老板 BaseAdapter封装 (一) 简单封装

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  6. 孟老板 BaseAdapter封装 (二) Healer,footer

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  7. 孟老板 BaseAdapter封装 (三) 空数据占位图

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  8. 孟老板 BaseAdapter封装(四) PageHelper

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  9. 孟老板 ListAdapter封装, 告别Adapter代码 (上)

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

随机推荐

  1. (9)MySQL进阶篇SQL优化(InnoDB锁-记录锁)

    1.概述 InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点MySQL与Oracle不同,后者是通过在数据块中对相应数据行加锁来实现的.InnoDB这种行锁实现特点意味着:只有通过索引条件检索 ...

  2. 如何通过Zoho Books门户管理供应商

    作为一个企业,不管规模大小,都有自己的供应商来为业务提供相关的服务和配件.随着采购的频率和供应商数量的增加,采购的管理和付款的跟踪难度就会增加,进而影响到企业和供应商之间的关系. 为了解决这个问题,Z ...

  3. Office·Word高级·VBA基础概念语法

    阅文时长 | 5.21分钟 字数统计 | 1823字符 『Office·Word高级·VBA基础概念语法』 编写人 | SCscHero 编写时间 | Monday, June 29, 2020 文章 ...

  4. jQuery 实现 全选/全不选/反选

    <button id="getall" class="btn btn-warning">全选</button><button id ...

  5. 用JIRA管理你的项目——(三)基于LDAP用户管理

    JIRA提供了基于LDAP方式的用户管理,也就是用户密码的管理交给LDAP,而JIRA只管理用户在系统中的角色. 要打开JIRA的LDAP设置,首先需要验证下你的LDAP服务是否正常! 几乎有所有的L ...

  6. [刷题] 237 Delete Nodes in a Linked List

    要求 给定链表中的一个节点,删除该节点 思路 通过改变节点的值实现  实现 1 struct ListNode { 2 int val; 3 ListNode *next; 4 ListNode(in ...

  7. [刷题] 209 Minimum Size Subarray Sum

    要求 给定一个含有 n 个正整数的数组和一个正整数 s 找出该数组中满足其和 ≥ s 的长度最小的连续子数组 如果不存在符合条件的连续子数组,返回 0 示例 输入:s = 7, nums = [2,3 ...

  8. python 运算及注释

    """ 上面的两行代码,在2.0的python中,若想执行含有汉字的代码,是必不可少的,且两行代码必写在单个py文件最上方,之间不可有任何字节.且该两行代码,可在pych ...

  9. rm删除破折号开头的文件或目录

    转载地址:http://blog.chinaunix.net/uid-25266990-id-3458755.html rm删除(清除)一个或多个文件 -f 选项将强制删除文件,即使这个文件是只读的. ...

  10. PHP相关session的知识

    由于http协议是一种无状态协议,所以没有办法在多个页面间保持一些信息.例如,用户的登录状态,不可能让用户每浏览一个页面登录一次.session就是为了解决一些需要在多页面间持久保持一种状态的机制.P ...