バックグラウンド
その日の仕事を辞める前に、上司はXiaozhuangを見つけました:「最適化されるページがあり、小さな需要があります、あなたはフォローアップします。」
Xiaozhuang:「良い上司!」彼はその時を見て、無意識のうちにプロトタイプを見つけて、これを見ました。 1ページ:
しばらく考えた後、Xiao Zhuangは特定の検索エンジンを巧みに開きましたが、適切なホイールが見つかりませんでした。XiaoZhuangは、ソフトウェア開発の最初のステップは、要件の分析と設計を行うことであり、袖をまくり上げることではないことを知っていました。そこで彼は、機能を分析してアイデアを整理することにしました。
警告:この記事は非常に長蛇の列であり、乾物はありません(fear.jpg)
分析
機能的解析
ページの一般的な機能:
- このページは特定のプロセスを示すリストであり、各リストアイテムのステータスは異なります(完了、進行中、未開始)。
- リストの片側には、タイムラインに似たビューがあり、各アイテムのステータスに応じて、異なる色の点と縦線が表示されます
詳細な分析
いずれかのアイテムのタイムラインビューの詳細は何ですか?
- まず、このタイムラインビューは、円と線の2つの主要部分で構成されていることがわかりました。
- すると当然、アイテムのタイムラインに、円の上の線(以下、上線)が緑色、円自体と円の下の線(以下、下線)の2色が再び表示されることがわかります。赤い
- つまり、このビューは、それ自体の色だけでなく、前のアイテムの色も知る必要があります。
- つまり、このビューの描画は、上線、円、下線の3つの部分に分割する必要があります。
- これは通常のミドルアイテムです。ただし、最初のアイテムと最後のアイテムについては、それぞれオンラインとオフラインではありません。
計画のアイデア
XiaoZhuangの心にはいくつかの考えがすぐに浮かびました。
- 最初のアイデアは、データの状態に応じてアダプターの色と線の表示を設定することです。
- しかし、そのような単純な円と線を理解するために、まだデザイナーが必要ですか?彼はそれがとても上手だと思われませんか?それは必要
Drawable
ですか?ただし、将来的に色を変えるのは面倒で、何枚か書いておく必要があります。したがって、このアイデアはすぐに通過しました
- しかし、そのような単純な円と線を理解するために、まだデザイナーが必要ですか?彼はそれがとても上手だと思われませんか?それは必要
- 2番目のアイデアは、カスタムビューを使用し、各アイテムに円と線を描画してから、カスタム属性を使用して色を設定することです。
- 彼はすぐにデモを書いて試してみましたが、その結果、彼のカスタムビューはスキルの習得が苦手で、難しい問題に直面したため[注]、泣き諦めました。
- 多分彼がドアを開ける運命にある:友達、多分あなたは
RecyclerView.ItemDecoration
それを聞いたことがありますか?
注:2000年後、問題をまったく再現できなかったことがわかりました。これは運命かもしれません。
RecyclerView.ItemDecorationの概要
これは強力なアーティファクトであり、リストに仕切りを追加するために使用されるのは、最も一般的で一般的な機能です。これは簡単な紹介であり、この記事の主な内容ではありません。あまりにも多くの効果を達成できるので、私はそれを学ぶことができません(ಥ_ಥ)
カスタムを実装するには、ItemDecoration
それを継承し、必要に応じて次の2つのメソッドを書き直す必要があります。
onDraw
:特定の描画コンテンツに使用- メソッドにはパラメータ
parent: RecyclerView
、リスト自体があるため、ここから各サブアイテムのコンテンツを取得できます - この方法の描画次元はリスト全体であるため、リストをトラバースし、位置を計算して、各子の描画を行う必要があることに注意してください。
- メソッドにはパラメータ
getItemOffsets
:アイテムの周囲のオフセットを制御するために使用され、onDrawで描画されるコンテンツはこれらの空白に描画されます- トップ、ようしかし、この方法の描画寸法は、各itemView用で左下、および右マージン各項目がある組
コーディングを開始
Xiao Zhuangは現在、基本的なアイデアと知識を備えており、IDEを開いてコーディングを開始します。しかし、ソフトウェア開発は反復的なプロセスであり、そのような小さな要件であっても、彼は単純なバージョンから始めることを計画しています。
初版
最初のバージョンでは、Xiao Zhuangは、状態や色を使用せずに、円と線の形状のみを描画することを目的としています。主な目的は、彼のアイデアが実現可能かどうかを確認することです。具体的な実装では、次のことを行う必要があります。
- 位置の計算とコンテンツの描画に関与する2つの重要な属性を定義する準備をします
radius
:円の半径を決定するために使用されますoffset
:ドットからitem
上部までの距離を示すために使用されます
- そして、左マージンに
getItemOffsets
タイムライン全体を描画するためのスペースをitem
残します - 最も重要な作業内容は、円と線を計算して描画したことです(特定の計算についてはコードを参照してください)
class FirstVerTimeline : RecyclerView.ItemDecoration() {
private val paint = Paint(Paint.ANTI_ALIAS_FLAG)
var radius = 8f
var offset = 15
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDraw(c, parent, state)
val count = parent.childCount
for (i in 0 until count) {
// 获取当前的itemView
val itemView = parent.getChildAt(i)
// 整个轴线的x坐标都是相同的
val xPosition = radius
// 画上线。第一个item不画
if (i != 0) {
c.drawLine(xPosition, itemView.top.toFloat(),
xPosition, itemView.top.toFloat() + offset, paint)
}
// 画下线。最后一个item不画
if (i != count - 1) {
c.drawLine(xPosition, itemView.top + radius * 2 + offset,
xPosition, itemView.bottom.toFloat(),paint)
}
// 画圆
c.drawCircle(xPosition, itemView.top + offset + radius, radius, paint)
}
}
override fun getItemOffsets(outRect: Rect, view: View, : RecyclerView, state: RecyclerView.State) {
super.getItemOffsets(outRect, view, parent, state)
// 设置item在左边的偏移量
outRect.left = radius.toInt() * 2
}
}
これで、仮想データソースRecord
を定義し、これItemDecoration
をRecyclerView
Shangkang効果に適用できます。
rv_timeline1.adapter = RecordAdapter(ArrayList<Record>())// 省略构造假数据
rv_timeline1.addItemDecoration(FirstVerTimeline())
形になり始めました!タイムラインとテキストの間に少しスクイーズがあるだけです。適切なパディングを追加し、テストデータを変更するだけで、本物のように見えます。
図1から図2に進むには、次のことを行う必要があります。
- 軸の左右のパディングを示すために使用される定義
paddingLeft
とpaddingRight
属性- オフセット位置
getItemOffsets
をoutRect.left = paddingLeft + paddingRight + radius.toInt() * 2
残すように変更 - 変更された
xPosition
初期値はradius + paddingLeft
、軸のx座標を変更することです。
- オフセット位置
最初のバージョンが完成したので、2番目のバージョンにはどのような新機能がありますか?↓↓↓
第2版
Xiao Zhuangは、第2版で州のさまざまな色を実装する予定です。この要求を実現するために、彼は深い熟考に陥りました。
- データクラスでカラーUIの実装を結合することは絶対に不可能であるため、状態からカラーを取得する方法が必要です。
- 描画するため
item
に知る必要item
単純なデータソースのリスト全体直接、色をdata
求心性ItemDecoration
良いです - 上記の2つのポイントを組み合わせることで、関数タイプの属性を定義できます
var color: (item: T) -> Int
。この属性を実装することにより、ユーザーはデータ状態を通じて目的の色を設定できます。
関数タイプは、Kotlin(または関数プログラミング)の特性の1つです。Javaの場合は、テンプレートモードの使用を検討できます。つまり、サブクラスを書き換えるための抽象メソッドを定義します。
class SecondVerTimeline<T> : RecyclerView.ItemDecoration() {
// 其他属性...
var data: List<T> = ArrayList() //-->这里有更新,定义了数据源
var color: (item: T) -> Int = { _ -> Color.GRAY } //-->这里有更新,通过这个属性设置颜色选择策略
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
super.onDraw(c, parent, state)
val count = parent.childCount
for (i in 0 until count) {
// ...
val adapterPosition = parent.getChildAdapterPosition(itemView) //-->这里有更新,获取当前项的真正位置
val item = data[adapterPosition] //-->这里有更新,获取当前项的数据源
// 画上线。第一个item不画
if (adapterPosition != 0) {
paint.color = color(data[adapterPosition - 1]) //-->这里有更新,设置上线的颜色
c.drawLine(...)
}
paint.color = color(item) //-->这里有更新,设置圆和下线的颜色
// 画下线。最后一个item不画
if (adapterPosition != data.size - 1) {//-->这里有更新,改用数据源的大小判断是否为最后一个item
c.drawLine(...)
}
// 画圆...
}
}
// getItemOffsets...
}
コードで注意が必要な点:
- 線を引く前に、それを取得し、
data数据源
それ上一个item
を使用しcolor属性
てその状態に対応する色を取得する必要があります - 円と下の線を描く前に変更する必要がある同じ
这一个item
色 parent.childCount
取得されるサブアイテムの数は画面の表示部分を参照しparent.getChildAdapterPosition
、オフラインで描画するかどうかを決定するには、リスト内のアイテムの実際の位置を取得する必要があります。そうしないと、[画面に表示されている最後のアイテムは実際の最後のアイテムではありませんが、オフラインではありませんが、下にスライドした後、再びオフラインになります]という恥ずかしいシーンがあります。- 現時点で最後かどうかを判断する
item
方法count - 1
がからに変更されdata.size - 1
、データソースのサイズを使用して比率を判断するcount
方が正確であることに注意してください(理由は前の方法と同じです)。
使用時にいくつかの変更もあります:
- の
data
セットItemDecoration
color
属性を介して色戦略を設定する
val secondVerTimeline = SecondVerTimeline<Record>()
secondVerTimeline.data = records
secondVerTimeline.color = { item ->
when (item.status) {
1 -> color1
2 -> color2
...
}
}
rv_timeline2.addItemDecoration(secondVerTimeline)
次に、実行して効果を確認できます。
うわー、ガチョウの女の子、あなたは状態に応じて色を変える機能を実現しました!第2版の機能も正常に実装されました!
あとがき
その後、Xiao ZhuangはUIに基づいて変更を加え、この要件をすばやく完了しました〜しかし、Xiao Zhuangは追求されたプログラマーであり、彼はこのコードのスケーラビリティと汎用性について考え始めました。あなたがしたくないかどうかは関係ありません、ただアヒルがまったくいないことを見つけることを考えてください!製品がドットを写真に変えたい場合はどうなりますか?それとも、製品は風の中でもっと自由に飛びたいですか?
ただし、一度に設計を行うことはできません。ソフトウェア作業の唯一の不変は変更です。同時に、いわゆる「将来発生する可能性のある変更」に対応して過剰設計を行うべきではありません。