Harmattanの標準に似たTimePickerを作成します

この投稿は、コンテスト「 Smart Posts for Smart Posts 」に参加しています





写真の左側では、標準のNokia MeeGo HarmattanアプリケーションでTimePicker (時間設定コンポーネント)がどのように見えるかを確認できます。 そして右側には、開発者向けに提供されているMeeGo Qt Components(Extras)TimePickerがあります。 顔の違い。



確かに、Nokiaが使用するコンポーネントは、より美しく機能的であるにもかかわらず、サードパーティのアプリケーション開発者が利用できないため、不公平があることが明らかになります。 正直なところ、私の意見では、これは私が見た中で最高のTimePickerオプションです。



そのため、このようなコンポーネントを自分で実装する方法を以下に示しますが、すべてが比較的単純であることがわかります。



QML



TimePickerとは何ですか? 実際、管理用の画像は3つ、タグが2つ、アクティブな領域が1つだけです。 これをQMLで描いてみましょう。

Item { id: timePicker width: 400 height: 400 property int hours: 0 property int minutes: 0 property alias backgroundImage: bg.source property alias hourDotImage: hourDot.source property alias minutesDotImage: minuteDot.source Image { id: bg anchors.fill: parent property int centerX: 200 property int centerY: 200 property int minuteRadius: 152 property int hourRadius: 65 property int minuteGradDelta: 6 property int hourGradDelta: 30 property int diameter: 73 Image { id: hourDot x: centerX y: centerY - bg.hourRadius width: bg.diameter height: bg.diameter Text { font.pixelSize: 40 anchors.centerIn: parent text: (timePicker.hours < 10 ? "0" : "") + timePicker.hours } } Image { id: minuteDot x: centerX y: centerY - bg.minuteRadius width: bg.diameter height: bg.diameter Text { font.pixelSize: 40 anchors.centerIn: parent color: "#CCCCCC" text: (timePicker.minutes < 10 ? "0" : "") + timePicker.minutes } } } MouseArea { id: mouseArea anchors.fill: parent } }
      
      





このコンポーネントを使用して、元のTimePickerと同じ円である画像として渡すと、00:00に設定されたアクションに応答しないコンポーネントが表示されます。



次に、変数の値に応じて、時間と分を含む円の位置が変わることを確認する必要があります。

  property int hours: 0 property int minutes: 0
      
      





これを行うには、 hourDotおよびminutesDotコンポーネントにそれぞれ次の行を追加します。

  x: (bg.centerX - bg.diameter / 2) + bg.hourRadius * Math.cos(timePicker.hours * bg.hourGradDelta * (3.14 / 180) - (90 * (3.14 / 180))) y: (bg.centerY - bg.diameter / 2) + bg.hourRadius * Math.sin(timePicker.hours * bg.hourGradDelta * (3.14 / 180) - (90 * (3.14 / 180)))
      
      





そして

  x: (bg.centerX - bg.diameter / 2) + bg.minuteRadius * Math.cos(timePicker.minutes * bg.minuteGradDelta * (3.14 / 180) - (90 * (3.14 / 180))) y: (bg.centerY - bg.diameter / 2) + bg.minuteRadius * Math.sin(timePicker.minutes * bg.minuteGradDelta * (3.14 / 180) - (90 * (3.14 / 180)))
      
      





超自然的なものはありません-円上の点の位置を見つけるだけです。



すべてを描画したら、コンポーネントの主要部分、つまりユーザーアクションとそれらに対する反応の処理に進みます。

処理中



MouseAreaは両方の円で一度に単独で動作しますが、これは丸いMouseAreaがないためです 。しかし、円とリング内の指の位置を処理する必要があります。 したがって、このタスクを自分自身にシフトし、 MouseAreaに対してこのような単純なメソッドを記述します。

 function chooseHandler(mouseX, mouseY) { if (bg.hourRadius + bg.diameter / 2 > Math.sqrt(Math.pow(bg.centerX - mouseX, 2) + Math.pow(bg.centerY - mouseY, 2))) return 0 else if (bg.minuteRadius + bg.diameter / 2 > Math.sqrt(Math.pow(bg.centerX - mouseX, 2) + Math.pow(bg.centerY - mouseY, 2))) return 1 return -1 }
      
      





このメソッドは、小さな円(時間)をヒットした場合は0、リング(分)をヒットした場合は1を返し、正方形の角を処理しないように何もヒットしなかった場合は-1を返します。



ユーザージェスチャを処理するには、3つのMouseAreaイベントが必要です

  onPressed: { currentHandler = chooseHandler(mouseX, mouseY) previousAlpha = findAlpha(mouseX, mouseY) }
      
      





上記のメソッドを呼び出して、作業対象を決定し、「12時間」に対する圧力点の角度を取得します。



  onReleased: { currentHandler = -1 previousAlpha = -1 }
      
      





すべてをリセットしてください。



  onPositionChanged: { var newAlpha = 0; if (currentHandler < 0) return newAlpha = findAlpha(mouseX, mouseY) if (currentHandler > 0) { timePicker.minutes = getNewTime(timePicker.minutes, newAlpha, bg.minuteGradDelta, 1) } else timePicker.hours = getNewTime(timePicker.hours, newAlpha, bg.hourGradDelta, 2) }
      
      





ユーザーの指の位置が変わったときに発生します。

新しい角度を取得します。すべてが比較的「12時間」です。特定のパラメーターを使用してgetNewTimeメソッドを呼び出します(以下で検討します)。



次に、 findAlphaメソッドを見てみましょう。これは単純であり、学校のジオメトリを超えたものでもありません。

  function findAlpha(x, y) { var alpha = (Math.atan((y - bg.centerY)/(x - bg.centerX)) * 180) / 3.14 + 90 if (x < bg.centerX) alpha += 180 return alpha }
      
      





ラジアン単位で角度を計算し、角度に変換し、すべての360度(最初の180度だけでなく)で動作するように追加のチェックを追加します。

getNewTime



このメソッドはコンピューティングの中核であり、次のパラメーターがあります。



実際の方法:

 function getNewTime(source, alpha, resolution, boundFactor) { var delta = alpha - previousAlpha if (Math.abs(delta) < resolution) return source if (Math.abs(delta) > 180) { delta = delta - sign(delta) * 360 } var result = source * resolution var resdel = Math.round(result + delta) if (Math.round(result + delta) > 359 * boundFactor) result += delta - 360 * (source * resolution > 359 ? boundFactor : 1) else if (Math.round(result + delta) < 0 * boundFactor) result += delta + 360 * (source * resolution > 359 ? boundFactor : boundFactor) else result += delta previousAlpha = alpha return result / resolution }
      
      





まず、現在の指と以前に保存した指の位置の差を計算します。差が1つのセクターのサイズより小さい場合は、何も変更せずに終了します。

次のチェックでは、0度(12時間)を通過した指が大きすぎるデルタを返した場合に処理し、修正します。

結果変数は、ポイントの初期位置に度単位で書き込まれ、その後にデルタが追加されます。

しかし、すべてがそれほど単純ではないため、条件のグループが境界線のケースを調整する役割を果たします。これらのチェックがなければ、TimePickerで25:68のような誤った値が表示される可能性があります。

その後、指の新しい位置を覚えて、正しい単位で結果を返します。

おわりに



実際、それだけです。 左側は、デバイスの最終的なスクリーンショットです。 与えられたアルゴリズムは完璧なものではなく、単なる実用的なソリューションです。 もっとうまくやれるなら、教えてください。

Gitoriusで利用可能なコンポーネントコード



PSこの問題の最初の( 間違った )解決策はさらに簡単でした。指の円の4分の1を見て、その動きの4つの方向のいずれかに応じて、1を加算または減算しました。 おそらく何らかの目的のために、このアプローチも理にかなっているかもしれませんが、既存のノキアのソリューションに可能な限り一致するようにタスクを設定しました。



All Articles