株式会社メドレーのブログ - TECH PLAY

TECH PLAY

株式会社メドレー

株式会社メドレー の技術ブログ

1412

はじめに こんにちは。デザイナーマエダです。 メドレーの クリエイターページ を最近リニューアルしたのですが、ご覧いただけましたでしょうか。 リニューアルまでの経緯は、デザインを担当した波切から後日このブログでエントリがあると思いますので詳細は割愛させていただきますが、リニューアル以前から「MEDLEY DESIGN & ENGINEERING」というサイトタイトルが使われているのは、みなさんご存じだったでしょうか。 今回はこの「MEDLEY DESIGN & ENGINEERING」という言葉に秘められた意味について TechLunch で発表したのですが、事前に社内のエンジニアにヒアリングしてみたところ、この言葉の意味に気付いてない人もいたので、あらためて紐解いてみました。 クリエイティブ業界の最近の風潮 今年 5 月に経済産業省・特許庁が「 デザイン経営 」宣言を公表しました。経営にデザインを活用した手法や効果、この手法を推進するための政策提言についてまとめられたものとなっており、ご存知の方もいらっしゃると思います。また、ジョン・マエダが発表した「 Design in Tech Report 2018 」などを見ても、特にインターネットテクノロジー業界におけるデザイナーのスタンスに変化が起こって来ているなと感じる昨今でもあります。 テクノロジー企業が急成長をしている中でも、特にデザインを強みとした企業が自社の独自性を出しつつ成長しているという状況で、今後どのようなスタイルのデザイナーが求められるのかという事を端的に示しているなと感じました。 他方で、インターネット業界でデザインに携わってそこそこの年数を経た私としては、クリエイティブを制作するうえで、デザインとテクノロジーは密接に関わっているという実感があり、常に 2 人 3 脚の関係性でお互い尊重しあいながらプロダクト開発をしてきました。 デザイン単体ではなくエンジニアリングと密に関わることでよいプロダクトになるという意識を持っているため、「デザイン」という概念だけで主張する風潮には、すこし違和感を覚える部分があったりします。 メドレーのクリエイティブ文化 メドレーはデザイン部という組織はありますが、プロダクトごとに個々デザイナーが担当しているため、デザイナー同士よりもエンジニアとのコミュニケーションが大半を占めます。 一方、プロダクトごとにビジネスモデルの理解や医療に関する知識など、基礎情報をインプットしておくことが重要ですので、十分な理解がないままに安易にペルソナを設計したりせず、事業をしっかり理解したうえでユーザーヒアリングや仮説検証を行うことも重視しています。 このようにメドレーのデザイナーは事業理解はもちろんのこと、テクノロジーの領域においても同様に理解をし、共通言語でコミュニケーションを取る必要があります。 メドレーのデザイナーは現在 3 名と少数ではありますが、BTC 人材として、それぞれ活躍しています。 「BTC 人材」とは、私が入社した当初からデザイナーとして意識している「ビジネス」「テクノロジー」「クリエイティブ」3 軸の領域を横断的に思考してデザインに結びつけるスタンスですが、ただ単に、自分の持つ領域を広げるだけではいいクリエイティブには繋がりません。あくまでデザイナーとしての本来の「軸」を持ちつつ、他の領域に対しても思考を広げていくことが重要なのですが、メドレーのデザイナーはこれを理解し実践しています。 エンジニアの方も逆に技術だけでなく、事業やクリエイティブへの理解を進め、プロダクトとしての質を高めるために、UI や UX の良し悪しについて我々デザイナーと議論をしています。よく聞くような、実装時にデザインとエンジニア同士で揉めたり、考え方の違いによるミスコミュニケーションに陥るということがありません。 より良いプロダクトを作るために、ユーザーに価値をどう提供すべきかお互いの領域に踏み込んでコミュニケーションがとれる文化は、デザイナー・エンジニアを含めたクリエイター組織として強みだと感じています。 まとめ このようにメドレーでは自分の専門分野と周辺分野を分け隔てることなく融合し、クリエイティブに結びつけようという文化が根付いています。この文化を一言で表わしているのが「MEDLEY DESIGN & ENGINEERING」という言葉になっているのです。 クリエイターページリニューアルの翌日に「 MEDLEY DRIVE 」のリリースもさせていただきました。このプロジェクトは医療ヘルスケア分野において、インターネットテクノロジーの活用を推進するための支援を目的としたプロジェクトです。 この MEDLEY DRIVE というプロジェクトを通して、医療の課題に対して、デザイナーがもっと活躍していく機会が増えていくと思うとワクワクします。 実際問題、カルテなどの医療システムは業務の性質上、複雑な構成になりやすかったりするのですが、そこにデザイナーが介入することによって医療機関や患者に対して、より価値あるプロダクトを推進していける分野でもあると思っています。 医療の課題に向き合って価値のあるプロダクトを創出したい方はもちろん、現在医療に興味がない方でも、気軽に弊社に遊びにきて話をしてみませんか? お知らせ メドレーでは、医療介護の人材採用システム「ジョブメドレー」や、医師たちがつくるオンライン医療事典「MEDLEY」、医療につよい介護施設・老人ホームの検索メディア「介護のほんね」、オンライン診療アプリ「CLINICS」などのプロダクトを提供しています。これらのサービスの拡大を受けて、その成長を支えるエンジニア・デザイナーを募集しています。 ちょっと興味があるという方も、ぜひお気軽にご連絡ください! www.medley.jp  
はじめに こんにちは。デザイナーマエダです。 メドレーの クリエイターページ を最近リニューアルしたのですが、ご覧いただけましたでしょうか。 リニューアルまでの経緯は、デザインを担当した波切から後日このブログでエントリがあると思いますので詳細は割愛させていただきますが、リニューアル以前から「MEDLEY DESIGN & ENGINEERING」というサイトタイトルが使われているのは、みなさんご存じだったでしょうか。 今回はこの「MEDLEY DESIGN & ENGINEERING」という言葉に秘められた意味について TechLunch で発表したのですが、事前に社内のエンジニアにヒアリングしてみたところ、この言葉の意味に気付いてない人もいたので、あらためて紐解いてみました。 クリエイティブ業界の最近の風潮 今年 5 月に経済産業省・特許庁が「 デザイン経営 」宣言を公表しました。経営にデザインを活用した手法や効果、この手法を推進するための政策提言についてまとめられたものとなっており、ご存知の方もいらっしゃると思います。また、ジョン・マエダが発表した「 Design in Tech Report 2018 」などを見ても、特にインターネットテクノロジー業界におけるデザイナーのスタンスに変化が起こって来ているなと感じる昨今でもあります。 テクノロジー企業が急成長をしている中でも、特にデザインを強みとした企業が自社の独自性を出しつつ成長しているという状況で、今後どのようなスタイルのデザイナーが求められるのかという事を端的に示しているなと感じました。 他方で、インターネット業界でデザインに携わってそこそこの年数を経た私としては、クリエイティブを制作するうえで、デザインとテクノロジーは密接に関わっているという実感があり、常に 2 人 3 脚の関係性でお互い尊重しあいながらプロダクト開発をしてきました。 デザイン単体ではなくエンジニアリングと密に関わることでよいプロダクトになるという意識を持っているため、「デザイン」という概念だけで主張する風潮には、すこし違和感を覚える部分があったりします。 メドレーのクリエイティブ文化 メドレーはデザイン部という組織はありますが、プロダクトごとに個々デザイナーが担当しているため、デザイナー同士よりもエンジニアとのコミュニケーションが大半を占めます。 一方、プロダクトごとにビジネスモデルの理解や医療に関する知識など、基礎情報をインプットしておくことが重要ですので、十分な理解がないままに安易にペルソナを設計したりせず、事業をしっかり理解したうえでユーザーヒアリングや仮説検証を行うことも重視しています。 このようにメドレーのデザイナーは事業理解はもちろんのこと、テクノロジーの領域においても同様に理解をし、共通言語でコミュニケーションを取る必要があります。 メドレーのデザイナーは現在 3 名と少数ではありますが、BTC 人材として、それぞれ活躍しています。 「BTC 人材」とは、私が入社した当初からデザイナーとして意識している「ビジネス」「テクノロジー」「クリエイティブ」3 軸の領域を横断的に思考してデザインに結びつけるスタンスですが、ただ単に、自分の持つ領域を広げるだけではいいクリエイティブには繋がりません。あくまでデザイナーとしての本来の「軸」を持ちつつ、他の領域に対しても思考を広げていくことが重要なのですが、メドレーのデザイナーはこれを理解し実践しています。 エンジニアの方も逆に技術だけでなく、事業やクリエイティブへの理解を進め、プロダクトとしての質を高めるために、UI や UX の良し悪しについて我々デザイナーと議論をしています。よく聞くような、実装時にデザインとエンジニア同士で揉めたり、考え方の違いによるミスコミュニケーションに陥るということがありません。 より良いプロダクトを作るために、ユーザーに価値をどう提供すべきかお互いの領域に踏み込んでコミュニケーションがとれる文化は、デザイナー・エンジニアを含めたクリエイター組織として強みだと感じています。 まとめ このようにメドレーでは自分の専門分野と周辺分野を分け隔てることなく融合し、クリエイティブに結びつけようという文化が根付いています。この文化を一言で表わしているのが「MEDLEY DESIGN & ENGINEERING」という言葉になっているのです。 クリエイターページリニューアルの翌日に「 MEDLEY DRIVE 」のリリースもさせていただきました。このプロジェクトは医療ヘルスケア分野において、インターネットテクノロジーの活用を推進するための支援を目的としたプロジェクトです。 この MEDLEY DRIVE というプロジェクトを通して、医療の課題に対して、デザイナーがもっと活躍していく機会が増えていくと思うとワクワクします。 実際問題、カルテなどの医療システムは業務の性質上、複雑な構成になりやすかったりするのですが、そこにデザイナーが介入することによって医療機関や患者に対して、より価値あるプロダクトを推進していける分野でもあると思っています。 医療の課題に向き合って価値のあるプロダクトを創出したい方はもちろん、現在医療に興味がない方でも、気軽に弊社に遊びにきて話をしてみませんか? お知らせ メドレーでは、医療介護の人材採用システム「ジョブメドレー」や、医師たちがつくるオンライン医療事典「MEDLEY」、医療につよい介護施設・老人ホームの検索メディア「介護のほんね」、オンライン診療アプリ「CLINICS」などのプロダクトを提供しています。これらのサービスの拡大を受けて、その成長を支えるエンジニア・デザイナーを募集しています。 ちょっと興味があるという方も、ぜひお気軽にご連絡ください! www.medley.jp  
はじめに こんにちは。デザイナーマエダです。 メドレーの クリエイターページ を最近リニューアルしたのですが、ご覧いただけましたでしょうか。 リニューアルまでの経緯は、デザインを担当した波切から後日このブログでエントリがあると思いますので詳細は割愛させていただきますが、リニューアル以前から「MEDLEY DESIGN & ENGINEERING」というサイトタイトルが使われているのは、みなさんご存じだったでしょうか。 今回はこの「MEDLEY DESIGN & ENGINEERING」という言葉に秘められた意味について TechLunch で発表したのですが、事前に社内のエンジニアにヒアリングしてみたところ、この言葉の意味に気付いてない人もいたので、あらためて紐解いてみました。 クリエイティブ業界の最近の風潮 今年 5 月に経済産業省・特許庁が「 デザイン経営 」宣言を公表しました。経営にデザインを活用した手法や効果、この手法を推進するための政策提言についてまとめられたものとなっており、ご存知の方もいらっしゃると思います。また、ジョン・マエダが発表した「 Design in Tech Report 2018 」などを見ても、特にインターネットテクノロジー業界におけるデザイナーのスタンスに変化が起こって来ているなと感じる昨今でもあります。 テクノロジー企業が急成長をしている中でも、特にデザインを強みとした企業が自社の独自性を出しつつ成長しているという状況で、今後どのようなスタイルのデザイナーが求められるのかという事を端的に示しているなと感じました。 他方で、インターネット業界でデザインに携わってそこそこの年数を経た私としては、クリエイティブを制作するうえで、デザインとテクノロジーは密接に関わっているという実感があり、常に 2 人 3 脚の関係性でお互い尊重しあいながらプロダクト開発をしてきました。 デザイン単体ではなくエンジニアリングと密に関わることでよいプロダクトになるという意識を持っているため、「デザイン」という概念だけで主張する風潮には、すこし違和感を覚える部分があったりします。 メドレーのクリエイティブ文化 メドレーはデザイン部という組織はありますが、プロダクトごとに個々デザイナーが担当しているため、デザイナー同士よりもエンジニアとのコミュニケーションが大半を占めます。 一方、プロダクトごとにビジネスモデルの理解や医療に関する知識など、基礎情報をインプットしておくことが重要ですので、十分な理解がないままに安易にペルソナを設計したりせず、事業をしっかり理解したうえでユーザーヒアリングや仮説検証を行うことも重視しています。 このようにメドレーのデザイナーは事業理解はもちろんのこと、テクノロジーの領域においても同様に理解をし、共通言語でコミュニケーションを取る必要があります。 メドレーのデザイナーは現在 3 名と少数ではありますが、BTC 人材として、それぞれ活躍しています。 「BTC 人材」とは、私が入社した当初からデザイナーとして意識している「ビジネス」「テクノロジー」「クリエイティブ」3 軸の領域を横断的に思考してデザインに結びつけるスタンスですが、ただ単に、自分の持つ領域を広げるだけではいいクリエイティブには繋がりません。あくまでデザイナーとしての本来の「軸」を持ちつつ、他の領域に対しても思考を広げていくことが重要なのですが、メドレーのデザイナーはこれを理解し実践しています。 エンジニアの方も逆に技術だけでなく、事業やクリエイティブへの理解を進め、プロダクトとしての質を高めるために、UI や UX の良し悪しについて我々デザイナーと議論をしています。よく聞くような、実装時にデザインとエンジニア同士で揉めたり、考え方の違いによるミスコミュニケーションに陥るということがありません。 より良いプロダクトを作るために、ユーザーに価値をどう提供すべきかお互いの領域に踏み込んでコミュニケーションがとれる文化は、デザイナー・エンジニアを含めたクリエイター組織として強みだと感じています。 まとめ このようにメドレーでは自分の専門分野と周辺分野を分け隔てることなく融合し、クリエイティブに結びつけようという文化が根付いています。この文化を一言で表わしているのが「MEDLEY DESIGN & ENGINEERING」という言葉になっているのです。 クリエイターページリニューアルの翌日に「 MEDLEY DRIVE 」のリリースもさせていただきました。このプロジェクトは医療ヘルスケア分野において、インターネットテクノロジーの活用を推進するための支援を目的としたプロジェクトです。 この MEDLEY DRIVE というプロジェクトを通して、医療の課題に対して、デザイナーがもっと活躍していく機会が増えていくと思うとワクワクします。 実際問題、カルテなどの医療システムは業務の性質上、複雑な構成になりやすかったりするのですが、そこにデザイナーが介入することによって医療機関や患者に対して、より価値あるプロダクトを推進していける分野でもあると思っています。 医療の課題に向き合って価値のあるプロダクトを創出したい方はもちろん、現在医療に興味がない方でも、気軽に弊社に遊びにきて話をしてみませんか? お知らせ メドレーでは、医療介護の人材採用システム「ジョブメドレー」や、医師たちがつくるオンライン医療事典「MEDLEY」、医療につよい介護施設・老人ホームの検索メディア「介護のほんね」、オンライン診療アプリ「CLINICS」などのプロダクトを提供しています。これらのサービスの拡大を受けて、その成長を支えるエンジニア・デザイナーを募集しています。 ちょっと興味があるという方も、ぜひお気軽にご連絡ください! www.medley.jp  
はじめに こんにちは。デザイナーマエダです。 メドレーの クリエイターページ を最近リニューアルしたのですが、ご覧いただけましたでしょうか。 リニューアルまでの経緯は、デザインを担当した波切から後日このブログでエントリがあると思いますので詳細は割愛させていただきますが、リニューアル以前から「MEDLEY DESIGN & ENGINEERING」というサイトタイトルが使われているのは、みなさんご存じだったでしょうか。 今回はこの「MEDLEY DESIGN & ENGINEERING」という言葉に秘められた意味について TechLunch で発表したのですが、事前に社内のエンジニアにヒアリングしてみたところ、この言葉の意味に気付いてない人もいたので、あらためて紐解いてみました。 クリエイティブ業界の最近の風潮 今年 5 月に経済産業省・特許庁が「 デザイン経営 」宣言を公表しました。経営にデザインを活用した手法や効果、この手法を推進するための政策提言についてまとめられたものとなっており、ご存知の方もいらっしゃると思います。また、ジョン・マエダが発表した「 Design in Tech Report 2018 」などを見ても、特にインターネットテクノロジー業界におけるデザイナーのスタンスに変化が起こって来ているなと感じる昨今でもあります。 テクノロジー企業が急成長をしている中でも、特にデザインを強みとした企業が自社の独自性を出しつつ成長しているという状況で、今後どのようなスタイルのデザイナーが求められるのかという事を端的に示しているなと感じました。 他方で、インターネット業界でデザインに携わってそこそこの年数を経た私としては、クリエイティブを制作するうえで、デザインとテクノロジーは密接に関わっているという実感があり、常に 2 人 3 脚の関係性でお互い尊重しあいながらプロダクト開発をしてきました。 デザイン単体ではなくエンジニアリングと密に関わることでよいプロダクトになるという意識を持っているため、「デザイン」という概念だけで主張する風潮には、すこし違和感を覚える部分があったりします。 メドレーのクリエイティブ文化 メドレーはデザイン部という組織はありますが、プロダクトごとに個々デザイナーが担当しているため、デザイナー同士よりもエンジニアとのコミュニケーションが大半を占めます。 一方、プロダクトごとにビジネスモデルの理解や医療に関する知識など、基礎情報をインプットしておくことが重要ですので、十分な理解がないままに安易にペルソナを設計したりせず、事業をしっかり理解したうえでユーザーヒアリングや仮説検証を行うことも重視しています。 このようにメドレーのデザイナーは事業理解はもちろんのこと、テクノロジーの領域においても同様に理解をし、共通言語でコミュニケーションを取る必要があります。 メドレーのデザイナーは現在 3 名と少数ではありますが、BTC 人材として、それぞれ活躍しています。 「BTC 人材」とは、私が入社した当初からデザイナーとして意識している「ビジネス」「テクノロジー」「クリエイティブ」3 軸の領域を横断的に思考してデザインに結びつけるスタンスですが、ただ単に、自分の持つ領域を広げるだけではいいクリエイティブには繋がりません。あくまでデザイナーとしての本来の「軸」を持ちつつ、他の領域に対しても思考を広げていくことが重要なのですが、メドレーのデザイナーはこれを理解し実践しています。 エンジニアの方も逆に技術だけでなく、事業やクリエイティブへの理解を進め、プロダクトとしての質を高めるために、UI や UX の良し悪しについて我々デザイナーと議論をしています。よく聞くような、実装時にデザインとエンジニア同士で揉めたり、考え方の違いによるミスコミュニケーションに陥るということがありません。 より良いプロダクトを作るために、ユーザーに価値をどう提供すべきかお互いの領域に踏み込んでコミュニケーションがとれる文化は、デザイナー・エンジニアを含めたクリエイター組織として強みだと感じています。 まとめ このようにメドレーでは自分の専門分野と周辺分野を分け隔てることなく融合し、クリエイティブに結びつけようという文化が根付いています。この文化を一言で表わしているのが「MEDLEY DESIGN & ENGINEERING」という言葉になっているのです。 クリエイターページリニューアルの翌日に「 MEDLEY DRIVE 」のリリースもさせていただきました。このプロジェクトは医療ヘルスケア分野において、インターネットテクノロジーの活用を推進するための支援を目的としたプロジェクトです。 この MEDLEY DRIVE というプロジェクトを通して、医療の課題に対して、デザイナーがもっと活躍していく機会が増えていくと思うとワクワクします。 実際問題、カルテなどの医療システムは業務の性質上、複雑な構成になりやすかったりするのですが、そこにデザイナーが介入することによって医療機関や患者に対して、より価値あるプロダクトを推進していける分野でもあると思っています。 医療の課題に向き合って価値のあるプロダクトを創出したい方はもちろん、現在医療に興味がない方でも、気軽に弊社に遊びにきて話をしてみませんか? お知らせ メドレーでは、医療介護の人材採用システム「ジョブメドレー」や、医師たちがつくるオンライン医療事典「MEDLEY」、医療につよい介護施設・老人ホームの検索メディア「介護のほんね」、オンライン診療アプリ「CLINICS」などのプロダクトを提供しています。これらのサービスの拡大を受けて、その成長を支えるエンジニア・デザイナーを募集しています。 ちょっと興味があるという方も、ぜひお気軽にご連絡ください! www.medley.jp  
こんにちは。開発本部の  CLINICS カルテ  の開発を担当している @seka です。メドレーでは貴重な (?) エンジニアの若者枠として日々奮闘しております。 今回、開発本部で定期的に開催している勉強会「TechLunch」で、「Android で HTML をいい感じで表示できるようにした話」 という題で発表しましたので、その内容について紹介させていただきます。 1. きっかけ 医師たちがつくるオンライン医療事典 MEDLEY (メドレー)  をアプリ化することができるか検証してみて欲しいという相談を受け、Android のモックを作成することになりました。 アプリらしい UI を目指して開発を進めていたのですが、MEDLEY では病気記事が CMS などに見られるような HTML 形式で管理されており、そのまま表示してもイメージしたようなデザインが実現できないかも…という課題に直面しました。 2. HTML を表示するまで いくつかのステップに分解して、HTML の要素を Android のコンポーネントに置換していくことで対応する方針を立て、その実現可能性を調べました。大まかなフローはこんな感じです。 事前に HTML と Android のコンポーネントの対応を決める HTML を Kotlin でも扱える形式に変換する HTML の要素を探索する マッチした要素を Android のコンポーネントで置き換える それぞれのフローについて解説していきます。 1. 事前に HTML と Android のコンポーネントの対応表を作る 簡単にですが下表のような対応を決めます。(検証段階だったので、いくつかの要素は省略しています) 2. HTML を Kotlin で扱える形式に変換する HTML を生の String として操作するのは流石に辛いので、Kotlin でも扱いやすいような形に変換します。 今回は  jhy/jsoup  という便利そうなライブラリを見つけたため、これを利用することにしました。 jsoup は Java 製の HTML パーサーで  example  にもあるように HTML を Kotlin でも扱いやすい形式に変換することができます。 下記の例は Wikipedia Root Node から小要素を探索していく様子です。 HTML タグの情報・親要素・内容のテキストを利用したいため保持しておきます。 val doc = Jsoup. connect ( "https://en.wikipedia.org/" ). get () doc. childNode ( 1 ). childNode ( 2 ). childNode ( 1 ) /* {Element@5739} "<div id="mw-page-base" class="noprint"></div>" attributes = {Attributes@5755} baseUri = "https://en.wikipedia.org/wiki/Main_Page" childNodes = {Collections$EmptyList@5720} size = 0 shadowChildrenRef = null tag = {Tag@5756} canContainInline = true empty = false formList = false formSubmit = false formatAsBlock = true isBlock = true preserveWhitespace = false selfClosing = false tagName = "div" parentNode = {Element@5729} siblingIndex = 1 */ 3. HTML の要素を探索する HTML の要素を探索する 再帰を利用して Root Node から小要素を探索し、jsoup で置き換えていきます。 class HTMLConverter ( private val html: String ) { fun parse () { val body = Jsoup. parse (html). normalise (). body () inspect (body, ElementViewHolder ()) } private fun inspect (parent: Element ) { parent. children (). forEach { if (parent.children. isEmpty ()) { return @forEach } inspect (it) } } 4. マッチした要素を Android のコンポーネントで置き換える 事前に定義した方法に従って、HTML をそれぞれ対応する Android コンポーネントに変換していきます。 Img 要素の場合: Glide を利用して画像を非同期で取得し ImageView にラップする。 private fun convertImage (el: Element ): View ? { val url = HttpUrl. parse (el. absUrl ( "src" )) ?: return null return ImageView (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) val header = LazyHeaders. Builder (). addHeader ( "Content-Type" , "image/bmp" ). build () val glideUrl = GlideUrl (url. url (), header) Glide. with (context) . asBitmap () . load (glideUrl) . into ( this ) } Table 要素の場合: Table と TableRow をそれぞれ作成して合成する。 private fun parseTable (el: Element ): View ? { return TableLayout (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) } } private fun createTableRow (): View ? { return TableRow (context) } private fun composeTableRow (self: TableRow , parent: TableLayout ): Boolean { table. addView (self) return true } Android Emulator で表示してみる 上記の方法で作成したモックがこちらになります。 ただ HTML を表示するだけであれば WebView を利用すればもっと楽に表示することもできますが、UI をカスタマイズしたい場合にはデータに変更を加えなければいけません。 今回のようなアプローチをとることで、もともとある HTML のデータを壊すことなく Android に適したデザインを実現しやすくなったのではないでしょうか。 さいごに コミュ障故に人前で話すことを避けてきたのですが、 TechLunch という機会をいただき Android なネタを発表をさせていただきました。 今回作成したモックは技術検証目的であるためリリースの予定はありませんが、発表のために書いた実装例は  seka/HTMLConverter.kt  として公開しています。 このようなアプローチを取る機会は少ないと思いますが、読んでくださった方の力になれれば幸いです!
こんにちは。開発本部の  CLINICS カルテ  の開発を担当している @seka です。メドレーでは貴重な (?) エンジニアの若者枠として日々奮闘しております。 今回、開発本部で定期的に開催している勉強会「TechLunch」で、「Android で HTML をいい感じで表示できるようにした話」 という題で発表しましたので、その内容について紹介させていただきます。 1. きっかけ 医師たちがつくるオンライン医療事典 MEDLEY (メドレー)  をアプリ化することができるか検証してみて欲しいという相談を受け、Android のモックを作成することになりました。 アプリらしい UI を目指して開発を進めていたのですが、MEDLEY では病気記事が CMS などに見られるような HTML 形式で管理されており、そのまま表示してもイメージしたようなデザインが実現できないかも…という課題に直面しました。 2. HTML を表示するまで いくつかのステップに分解して、HTML の要素を Android のコンポーネントに置換していくことで対応する方針を立て、その実現可能性を調べました。大まかなフローはこんな感じです。 事前に HTML と Android のコンポーネントの対応を決める HTML を Kotlin でも扱える形式に変換する HTML の要素を探索する マッチした要素を Android のコンポーネントで置き換える それぞれのフローについて解説していきます。 1. 事前に HTML と Android のコンポーネントの対応表を作る 簡単にですが下表のような対応を決めます。(検証段階だったので、いくつかの要素は省略しています) 2. HTML を Kotlin で扱える形式に変換する HTML を生の String として操作するのは流石に辛いので、Kotlin でも扱いやすいような形に変換します。 今回は  jhy/jsoup  という便利そうなライブラリを見つけたため、これを利用することにしました。 jsoup は Java 製の HTML パーサーで  example  にもあるように HTML を Kotlin でも扱いやすい形式に変換することができます。 下記の例は Wikipedia Root Node から小要素を探索していく様子です。 HTML タグの情報・親要素・内容のテキストを利用したいため保持しておきます。 val doc = Jsoup. connect ( "https://en.wikipedia.org/" ). get () doc. childNode ( 1 ). childNode ( 2 ). childNode ( 1 ) /* {Element@5739} "<div id="mw-page-base" class="noprint"></div>" attributes = {Attributes@5755} baseUri = "https://en.wikipedia.org/wiki/Main_Page" childNodes = {Collections$EmptyList@5720} size = 0 shadowChildrenRef = null tag = {Tag@5756} canContainInline = true empty = false formList = false formSubmit = false formatAsBlock = true isBlock = true preserveWhitespace = false selfClosing = false tagName = "div" parentNode = {Element@5729} siblingIndex = 1 */ 3. HTML の要素を探索する HTML の要素を探索する 再帰を利用して Root Node から小要素を探索し、jsoup で置き換えていきます。 class HTMLConverter ( private val html: String ) { fun parse () { val body = Jsoup. parse (html). normalise (). body () inspect (body, ElementViewHolder ()) } private fun inspect (parent: Element ) { parent. children (). forEach { if (parent.children. isEmpty ()) { return @forEach } inspect (it) } } 4. マッチした要素を Android のコンポーネントで置き換える 事前に定義した方法に従って、HTML をそれぞれ対応する Android コンポーネントに変換していきます。 Img 要素の場合: Glide を利用して画像を非同期で取得し ImageView にラップする。 private fun convertImage (el: Element ): View ? { val url = HttpUrl. parse (el. absUrl ( "src" )) ?: return null return ImageView (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) val header = LazyHeaders. Builder (). addHeader ( "Content-Type" , "image/bmp" ). build () val glideUrl = GlideUrl (url. url (), header) Glide. with (context) . asBitmap () . load (glideUrl) . into ( this ) } Table 要素の場合: Table と TableRow をそれぞれ作成して合成する。 private fun parseTable (el: Element ): View ? { return TableLayout (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) } } private fun createTableRow (): View ? { return TableRow (context) } private fun composeTableRow (self: TableRow , parent: TableLayout ): Boolean { table. addView (self) return true } Android Emulator で表示してみる 上記の方法で作成したモックがこちらになります。 ただ HTML を表示するだけであれば WebView を利用すればもっと楽に表示することもできますが、UI をカスタマイズしたい場合にはデータに変更を加えなければいけません。 今回のようなアプローチをとることで、もともとある HTML のデータを壊すことなく Android に適したデザインを実現しやすくなったのではないでしょうか。 さいごに コミュ障故に人前で話すことを避けてきたのですが、 TechLunch という機会をいただき Android なネタを発表をさせていただきました。 今回作成したモックは技術検証目的であるためリリースの予定はありませんが、発表のために書いた実装例は  seka/HTMLConverter.kt  として公開しています。 このようなアプローチを取る機会は少ないと思いますが、読んでくださった方の力になれれば幸いです!
こんにちは。開発本部の  CLINICS カルテ  の開発を担当している @seka です。メドレーでは貴重な (?) エンジニアの若者枠として日々奮闘しております。 今回、開発本部で定期的に開催している勉強会「TechLunch」で、「Android で HTML をいい感じで表示できるようにした話」 という題で発表しましたので、その内容について紹介させていただきます。 1. きっかけ 医師たちがつくるオンライン医療事典 MEDLEY (メドレー)  をアプリ化することができるか検証してみて欲しいという相談を受け、Android のモックを作成することになりました。 アプリらしい UI を目指して開発を進めていたのですが、MEDLEY では病気記事が CMS などに見られるような HTML 形式で管理されており、そのまま表示してもイメージしたようなデザインが実現できないかも…という課題に直面しました。 2. HTML を表示するまで いくつかのステップに分解して、HTML の要素を Android のコンポーネントに置換していくことで対応する方針を立て、その実現可能性を調べました。大まかなフローはこんな感じです。 事前に HTML と Android のコンポーネントの対応を決める HTML を Kotlin でも扱える形式に変換する HTML の要素を探索する マッチした要素を Android のコンポーネントで置き換える それぞれのフローについて解説していきます。 1. 事前に HTML と Android のコンポーネントの対応表を作る 簡単にですが下表のような対応を決めます。(検証段階だったので、いくつかの要素は省略しています) 2. HTML を Kotlin で扱える形式に変換する HTML を生の String として操作するのは流石に辛いので、Kotlin でも扱いやすいような形に変換します。 今回は  jhy/jsoup  という便利そうなライブラリを見つけたため、これを利用することにしました。 jsoup は Java 製の HTML パーサーで  example  にもあるように HTML を Kotlin でも扱いやすい形式に変換することができます。 下記の例は Wikipedia Root Node から小要素を探索していく様子です。 HTML タグの情報・親要素・内容のテキストを利用したいため保持しておきます。 val doc = Jsoup. connect ( "https://en.wikipedia.org/" ). get () doc. childNode ( 1 ). childNode ( 2 ). childNode ( 1 ) /* {Element@5739} "<div id="mw-page-base" class="noprint"></div>" attributes = {Attributes@5755} baseUri = "https://en.wikipedia.org/wiki/Main_Page" childNodes = {Collections$EmptyList@5720} size = 0 shadowChildrenRef = null tag = {Tag@5756} canContainInline = true empty = false formList = false formSubmit = false formatAsBlock = true isBlock = true preserveWhitespace = false selfClosing = false tagName = "div" parentNode = {Element@5729} siblingIndex = 1 */ 3. HTML の要素を探索する HTML の要素を探索する 再帰を利用して Root Node から小要素を探索し、jsoup で置き換えていきます。 class HTMLConverter ( private val html: String ) { fun parse () { val body = Jsoup. parse (html). normalise (). body () inspect (body, ElementViewHolder ()) } private fun inspect (parent: Element ) { parent. children (). forEach { if (parent.children. isEmpty ()) { return @forEach } inspect (it) } } 4. マッチした要素を Android のコンポーネントで置き換える 事前に定義した方法に従って、HTML をそれぞれ対応する Android コンポーネントに変換していきます。 Img 要素の場合: Glide を利用して画像を非同期で取得し ImageView にラップする。 private fun convertImage (el: Element ): View ? { val url = HttpUrl. parse (el. absUrl ( "src" )) ?: return null return ImageView (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) val header = LazyHeaders. Builder (). addHeader ( "Content-Type" , "image/bmp" ). build () val glideUrl = GlideUrl (url. url (), header) Glide. with (context) . asBitmap () . load (glideUrl) . into ( this ) } Table 要素の場合: Table と TableRow をそれぞれ作成して合成する。 private fun parseTable (el: Element ): View ? { return TableLayout (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) } } private fun createTableRow (): View ? { return TableRow (context) } private fun composeTableRow (self: TableRow , parent: TableLayout ): Boolean { table. addView (self) return true } Android Emulator で表示してみる 上記の方法で作成したモックがこちらになります。 ただ HTML を表示するだけであれば WebView を利用すればもっと楽に表示することもできますが、UI をカスタマイズしたい場合にはデータに変更を加えなければいけません。 今回のようなアプローチをとることで、もともとある HTML のデータを壊すことなく Android に適したデザインを実現しやすくなったのではないでしょうか。 さいごに コミュ障故に人前で話すことを避けてきたのですが、 TechLunch という機会をいただき Android なネタを発表をさせていただきました。 今回作成したモックは技術検証目的であるためリリースの予定はありませんが、発表のために書いた実装例は  seka/HTMLConverter.kt  として公開しています。 このようなアプローチを取る機会は少ないと思いますが、読んでくださった方の力になれれば幸いです!
こんにちは。開発本部の  CLINICS カルテ  の開発を担当している @seka です。メドレーでは貴重な (?) エンジニアの若者枠として日々奮闘しております。 今回、開発本部で定期的に開催している勉強会「TechLunch」で、「Android で HTML をいい感じで表示できるようにした話」 という題で発表しましたので、その内容について紹介させていただきます。 1. きっかけ 医師たちがつくるオンライン医療事典 MEDLEY (メドレー)  をアプリ化することができるか検証してみて欲しいという相談を受け、Android のモックを作成することになりました。 アプリらしい UI を目指して開発を進めていたのですが、MEDLEY では病気記事が CMS などに見られるような HTML 形式で管理されており、そのまま表示してもイメージしたようなデザインが実現できないかも…という課題に直面しました。 2. HTML を表示するまで いくつかのステップに分解して、HTML の要素を Android のコンポーネントに置換していくことで対応する方針を立て、その実現可能性を調べました。大まかなフローはこんな感じです。 事前に HTML と Android のコンポーネントの対応を決める HTML を Kotlin でも扱える形式に変換する HTML の要素を探索する マッチした要素を Android のコンポーネントで置き換える それぞれのフローについて解説していきます。 1. 事前に HTML と Android のコンポーネントの対応表を作る 簡単にですが下表のような対応を決めます。(検証段階だったので、いくつかの要素は省略しています) 2. HTML を Kotlin で扱える形式に変換する HTML を生の String として操作するのは流石に辛いので、Kotlin でも扱いやすいような形に変換します。 今回は  jhy/jsoup  という便利そうなライブラリを見つけたため、これを利用することにしました。 jsoup は Java 製の HTML パーサーで  example  にもあるように HTML を Kotlin でも扱いやすい形式に変換することができます。 下記の例は Wikipedia Root Node から小要素を探索していく様子です。 HTML タグの情報・親要素・内容のテキストを利用したいため保持しておきます。 val doc = Jsoup. connect ( "https://en.wikipedia.org/" ). get () doc. childNode ( 1 ). childNode ( 2 ). childNode ( 1 ) /* {Element@5739} "<div id="mw-page-base" class="noprint"></div>" attributes = {Attributes@5755} baseUri = "https://en.wikipedia.org/wiki/Main_Page" childNodes = {Collections$EmptyList@5720} size = 0 shadowChildrenRef = null tag = {Tag@5756} canContainInline = true empty = false formList = false formSubmit = false formatAsBlock = true isBlock = true preserveWhitespace = false selfClosing = false tagName = "div" parentNode = {Element@5729} siblingIndex = 1 */ 3. HTML の要素を探索する HTML の要素を探索する 再帰を利用して Root Node から小要素を探索し、jsoup で置き換えていきます。 class HTMLConverter ( private val html: String ) { fun parse () { val body = Jsoup. parse (html). normalise (). body () inspect (body, ElementViewHolder ()) } private fun inspect (parent: Element ) { parent. children (). forEach { if (parent.children. isEmpty ()) { return @forEach } inspect (it) } } 4. マッチした要素を Android のコンポーネントで置き換える 事前に定義した方法に従って、HTML をそれぞれ対応する Android コンポーネントに変換していきます。 Img 要素の場合: Glide を利用して画像を非同期で取得し ImageView にラップする。 private fun convertImage (el: Element ): View ? { val url = HttpUrl. parse (el. absUrl ( "src" )) ?: return null return ImageView (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) val header = LazyHeaders. Builder (). addHeader ( "Content-Type" , "image/bmp" ). build () val glideUrl = GlideUrl (url. url (), header) Glide. with (context) . asBitmap () . load (glideUrl) . into ( this ) } Table 要素の場合: Table と TableRow をそれぞれ作成して合成する。 private fun parseTable (el: Element ): View ? { return TableLayout (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) } } private fun createTableRow (): View ? { return TableRow (context) } private fun composeTableRow (self: TableRow , parent: TableLayout ): Boolean { table. addView (self) return true } Android Emulator で表示してみる 上記の方法で作成したモックがこちらになります。 ただ HTML を表示するだけであれば WebView を利用すればもっと楽に表示することもできますが、UI をカスタマイズしたい場合にはデータに変更を加えなければいけません。 今回のようなアプローチをとることで、もともとある HTML のデータを壊すことなく Android に適したデザインを実現しやすくなったのではないでしょうか。 さいごに コミュ障故に人前で話すことを避けてきたのですが、 TechLunch という機会をいただき Android なネタを発表をさせていただきました。 今回作成したモックは技術検証目的であるためリリースの予定はありませんが、発表のために書いた実装例は  seka/HTMLConverter.kt  として公開しています。 このようなアプローチを取る機会は少ないと思いますが、読んでくださった方の力になれれば幸いです!
こんにちは。開発本部の  CLINICS カルテ  の開発を担当している @seka です。メドレーでは貴重な (?) エンジニアの若者枠として日々奮闘しております。 今回、開発本部で定期的に開催している勉強会「TechLunch」で、「Android で HTML をいい感じで表示できるようにした話」 という題で発表しましたので、その内容について紹介させていただきます。 1. きっかけ 医師たちがつくるオンライン医療事典 MEDLEY (メドレー)  をアプリ化することができるか検証してみて欲しいという相談を受け、Android のモックを作成することになりました。 アプリらしい UI を目指して開発を進めていたのですが、MEDLEY では病気記事が CMS などに見られるような HTML 形式で管理されており、そのまま表示してもイメージしたようなデザインが実現できないかも…という課題に直面しました。 2. HTML を表示するまで いくつかのステップに分解して、HTML の要素を Android のコンポーネントに置換していくことで対応する方針を立て、その実現可能性を調べました。大まかなフローはこんな感じです。 事前に HTML と Android のコンポーネントの対応を決める HTML を Kotlin でも扱える形式に変換する HTML の要素を探索する マッチした要素を Android のコンポーネントで置き換える それぞれのフローについて解説していきます。 1. 事前に HTML と Android のコンポーネントの対応表を作る 簡単にですが下表のような対応を決めます。(検証段階だったので、いくつかの要素は省略しています) 2. HTML を Kotlin で扱える形式に変換する HTML を生の String として操作するのは流石に辛いので、Kotlin でも扱いやすいような形に変換します。 今回は  jhy/jsoup  という便利そうなライブラリを見つけたため、これを利用することにしました。 jsoup は Java 製の HTML パーサーで  example  にもあるように HTML を Kotlin でも扱いやすい形式に変換することができます。 下記の例は Wikipedia Root Node から小要素を探索していく様子です。 HTML タグの情報・親要素・内容のテキストを利用したいため保持しておきます。 val doc = Jsoup. connect ( "https://en.wikipedia.org/" ). get () doc. childNode ( 1 ). childNode ( 2 ). childNode ( 1 ) /* {Element@5739} "<div id="mw-page-base" class="noprint"></div>" attributes = {Attributes@5755} baseUri = "https://en.wikipedia.org/wiki/Main_Page" childNodes = {Collections$EmptyList@5720} size = 0 shadowChildrenRef = null tag = {Tag@5756} canContainInline = true empty = false formList = false formSubmit = false formatAsBlock = true isBlock = true preserveWhitespace = false selfClosing = false tagName = "div" parentNode = {Element@5729} siblingIndex = 1 */ 3. HTML の要素を探索する HTML の要素を探索する 再帰を利用して Root Node から小要素を探索し、jsoup で置き換えていきます。 class HTMLConverter ( private val html: String ) { fun parse () { val body = Jsoup. parse (html). normalise (). body () inspect (body, ElementViewHolder ()) } private fun inspect (parent: Element ) { parent. children (). forEach { if (parent.children. isEmpty ()) { return @forEach } inspect (it) } } 4. マッチした要素を Android のコンポーネントで置き換える 事前に定義した方法に従って、HTML をそれぞれ対応する Android コンポーネントに変換していきます。 Img 要素の場合: Glide を利用して画像を非同期で取得し ImageView にラップする。 private fun convertImage (el: Element ): View ? { val url = HttpUrl. parse (el. absUrl ( "src" )) ?: return null return ImageView (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) val header = LazyHeaders. Builder (). addHeader ( "Content-Type" , "image/bmp" ). build () val glideUrl = GlideUrl (url. url (), header) Glide. with (context) . asBitmap () . load (glideUrl) . into ( this ) } Table 要素の場合: Table と TableRow をそれぞれ作成して合成する。 private fun parseTable (el: Element ): View ? { return TableLayout (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) } } private fun createTableRow (): View ? { return TableRow (context) } private fun composeTableRow (self: TableRow , parent: TableLayout ): Boolean { table. addView (self) return true } Android Emulator で表示してみる 上記の方法で作成したモックがこちらになります。 ただ HTML を表示するだけであれば WebView を利用すればもっと楽に表示することもできますが、UI をカスタマイズしたい場合にはデータに変更を加えなければいけません。 今回のようなアプローチをとることで、もともとある HTML のデータを壊すことなく Android に適したデザインを実現しやすくなったのではないでしょうか。 さいごに コミュ障故に人前で話すことを避けてきたのですが、 TechLunch という機会をいただき Android なネタを発表をさせていただきました。 今回作成したモックは技術検証目的であるためリリースの予定はありませんが、発表のために書いた実装例は  seka/HTMLConverter.kt  として公開しています。 このようなアプローチを取る機会は少ないと思いますが、読んでくださった方の力になれれば幸いです!
こんにちは。開発本部の  CLINICS カルテ  の開発を担当している @seka です。メドレーでは貴重な (?) エンジニアの若者枠として日々奮闘しております。 今回、開発本部で定期的に開催している勉強会「TechLunch」で、「Android で HTML をいい感じで表示できるようにした話」 という題で発表しましたので、その内容について紹介させていただきます。 1. きっかけ 医師たちがつくるオンライン医療事典 MEDLEY (メドレー)  をアプリ化することができるか検証してみて欲しいという相談を受け、Android のモックを作成することになりました。 アプリらしい UI を目指して開発を進めていたのですが、MEDLEY では病気記事が CMS などに見られるような HTML 形式で管理されており、そのまま表示してもイメージしたようなデザインが実現できないかも…という課題に直面しました。 2. HTML を表示するまで いくつかのステップに分解して、HTML の要素を Android のコンポーネントに置換していくことで対応する方針を立て、その実現可能性を調べました。大まかなフローはこんな感じです。 事前に HTML と Android のコンポーネントの対応を決める HTML を Kotlin でも扱える形式に変換する HTML の要素を探索する マッチした要素を Android のコンポーネントで置き換える それぞれのフローについて解説していきます。 1. 事前に HTML と Android のコンポーネントの対応表を作る 簡単にですが下表のような対応を決めます。(検証段階だったので、いくつかの要素は省略しています) 2. HTML を Kotlin で扱える形式に変換する HTML を生の String として操作するのは流石に辛いので、Kotlin でも扱いやすいような形に変換します。 今回は  jhy/jsoup  という便利そうなライブラリを見つけたため、これを利用することにしました。 jsoup は Java 製の HTML パーサーで  example  にもあるように HTML を Kotlin でも扱いやすい形式に変換することができます。 下記の例は Wikipedia Root Node から小要素を探索していく様子です。 HTML タグの情報・親要素・内容のテキストを利用したいため保持しておきます。 val doc = Jsoup. connect ( "https://en.wikipedia.org/" ). get () doc. childNode ( 1 ). childNode ( 2 ). childNode ( 1 ) /* {Element@5739} "<div id="mw-page-base" class="noprint"></div>" attributes = {Attributes@5755} baseUri = "https://en.wikipedia.org/wiki/Main_Page" childNodes = {Collections$EmptyList@5720} size = 0 shadowChildrenRef = null tag = {Tag@5756} canContainInline = true empty = false formList = false formSubmit = false formatAsBlock = true isBlock = true preserveWhitespace = false selfClosing = false tagName = "div" parentNode = {Element@5729} siblingIndex = 1 */ 3. HTML の要素を探索する HTML の要素を探索する 再帰を利用して Root Node から小要素を探索し、jsoup で置き換えていきます。 class HTMLConverter ( private val html: String ) { fun parse () { val body = Jsoup. parse (html). normalise (). body () inspect (body, ElementViewHolder ()) } private fun inspect (parent: Element ) { parent. children (). forEach { if (parent.children. isEmpty ()) { return @forEach } inspect (it) } } 4. マッチした要素を Android のコンポーネントで置き換える 事前に定義した方法に従って、HTML をそれぞれ対応する Android コンポーネントに変換していきます。 Img 要素の場合: Glide を利用して画像を非同期で取得し ImageView にラップする。 private fun convertImage (el: Element ): View ? { val url = HttpUrl. parse (el. absUrl ( "src" )) ?: return null return ImageView (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) val header = LazyHeaders. Builder (). addHeader ( "Content-Type" , "image/bmp" ). build () val glideUrl = GlideUrl (url. url (), header) Glide. with (context) . asBitmap () . load (glideUrl) . into ( this ) } Table 要素の場合: Table と TableRow をそれぞれ作成して合成する。 private fun parseTable (el: Element ): View ? { return TableLayout (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) } } private fun createTableRow (): View ? { return TableRow (context) } private fun composeTableRow (self: TableRow , parent: TableLayout ): Boolean { table. addView (self) return true } Android Emulator で表示してみる 上記の方法で作成したモックがこちらになります。 ただ HTML を表示するだけであれば WebView を利用すればもっと楽に表示することもできますが、UI をカスタマイズしたい場合にはデータに変更を加えなければいけません。 今回のようなアプローチをとることで、もともとある HTML のデータを壊すことなく Android に適したデザインを実現しやすくなったのではないでしょうか。 さいごに コミュ障故に人前で話すことを避けてきたのですが、 TechLunch という機会をいただき Android なネタを発表をさせていただきました。 今回作成したモックは技術検証目的であるためリリースの予定はありませんが、発表のために書いた実装例は  seka/HTMLConverter.kt  として公開しています。 このようなアプローチを取る機会は少ないと思いますが、読んでくださった方の力になれれば幸いです!
こんにちは。開発本部の  CLINICS カルテ  の開発を担当している @seka です。メドレーでは貴重な (?) エンジニアの若者枠として日々奮闘しております。 今回、開発本部で定期的に開催している勉強会「TechLunch」で、「Android で HTML をいい感じで表示できるようにした話」 という題で発表しましたので、その内容について紹介させていただきます。 1. きっかけ 医師たちがつくるオンライン医療事典 MEDLEY (メドレー)  をアプリ化することができるか検証してみて欲しいという相談を受け、Android のモックを作成することになりました。 アプリらしい UI を目指して開発を進めていたのですが、MEDLEY では病気記事が CMS などに見られるような HTML 形式で管理されており、そのまま表示してもイメージしたようなデザインが実現できないかも…という課題に直面しました。 2. HTML を表示するまで いくつかのステップに分解して、HTML の要素を Android のコンポーネントに置換していくことで対応する方針を立て、その実現可能性を調べました。大まかなフローはこんな感じです。 事前に HTML と Android のコンポーネントの対応を決める HTML を Kotlin でも扱える形式に変換する HTML の要素を探索する マッチした要素を Android のコンポーネントで置き換える それぞれのフローについて解説していきます。 1. 事前に HTML と Android のコンポーネントの対応表を作る 簡単にですが下表のような対応を決めます。(検証段階だったので、いくつかの要素は省略しています) 2. HTML を Kotlin で扱える形式に変換する HTML を生の String として操作するのは流石に辛いので、Kotlin でも扱いやすいような形に変換します。 今回は  jhy/jsoup  という便利そうなライブラリを見つけたため、これを利用することにしました。 jsoup は Java 製の HTML パーサーで  example  にもあるように HTML を Kotlin でも扱いやすい形式に変換することができます。 下記の例は Wikipedia Root Node から小要素を探索していく様子です。 HTML タグの情報・親要素・内容のテキストを利用したいため保持しておきます。 val doc = Jsoup. connect ( "https://en.wikipedia.org/" ). get () doc. childNode ( 1 ). childNode ( 2 ). childNode ( 1 ) /* {Element@5739} "<div id="mw-page-base" class="noprint"></div>" attributes = {Attributes@5755} baseUri = "https://en.wikipedia.org/wiki/Main_Page" childNodes = {Collections$EmptyList@5720} size = 0 shadowChildrenRef = null tag = {Tag@5756} canContainInline = true empty = false formList = false formSubmit = false formatAsBlock = true isBlock = true preserveWhitespace = false selfClosing = false tagName = "div" parentNode = {Element@5729} siblingIndex = 1 */ 3. HTML の要素を探索する HTML の要素を探索する 再帰を利用して Root Node から小要素を探索し、jsoup で置き換えていきます。 class HTMLConverter ( private val html: String ) { fun parse () { val body = Jsoup. parse (html). normalise (). body () inspect (body, ElementViewHolder ()) } private fun inspect (parent: Element ) { parent. children (). forEach { if (parent.children. isEmpty ()) { return @forEach } inspect (it) } } 4. マッチした要素を Android のコンポーネントで置き換える 事前に定義した方法に従って、HTML をそれぞれ対応する Android コンポーネントに変換していきます。 Img 要素の場合: Glide を利用して画像を非同期で取得し ImageView にラップする。 private fun convertImage (el: Element ): View ? { val url = HttpUrl. parse (el. absUrl ( "src" )) ?: return null return ImageView (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) val header = LazyHeaders. Builder (). addHeader ( "Content-Type" , "image/bmp" ). build () val glideUrl = GlideUrl (url. url (), header) Glide. with (context) . asBitmap () . load (glideUrl) . into ( this ) } Table 要素の場合: Table と TableRow をそれぞれ作成して合成する。 private fun parseTable (el: Element ): View ? { return TableLayout (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) } } private fun createTableRow (): View ? { return TableRow (context) } private fun composeTableRow (self: TableRow , parent: TableLayout ): Boolean { table. addView (self) return true } Android Emulator で表示してみる 上記の方法で作成したモックがこちらになります。 ただ HTML を表示するだけであれば WebView を利用すればもっと楽に表示することもできますが、UI をカスタマイズしたい場合にはデータに変更を加えなければいけません。 今回のようなアプローチをとることで、もともとある HTML のデータを壊すことなく Android に適したデザインを実現しやすくなったのではないでしょうか。 さいごに コミュ障故に人前で話すことを避けてきたのですが、 TechLunch という機会をいただき Android なネタを発表をさせていただきました。 今回作成したモックは技術検証目的であるためリリースの予定はありませんが、発表のために書いた実装例は  seka/HTMLConverter.kt  として公開しています。 このようなアプローチを取る機会は少ないと思いますが、読んでくださった方の力になれれば幸いです!
こんにちは。開発本部の  CLINICS カルテ  の開発を担当している @seka です。メドレーでは貴重な (?) エンジニアの若者枠として日々奮闘しております。 今回、開発本部で定期的に開催している勉強会「TechLunch」で、「Android で HTML をいい感じで表示できるようにした話」 という題で発表しましたので、その内容について紹介させていただきます。 1. きっかけ 医師たちがつくるオンライン医療事典 MEDLEY (メドレー)  をアプリ化することができるか検証してみて欲しいという相談を受け、Android のモックを作成することになりました。 アプリらしい UI を目指して開発を進めていたのですが、MEDLEY では病気記事が CMS などに見られるような HTML 形式で管理されており、そのまま表示してもイメージしたようなデザインが実現できないかも…という課題に直面しました。 2. HTML を表示するまで いくつかのステップに分解して、HTML の要素を Android のコンポーネントに置換していくことで対応する方針を立て、その実現可能性を調べました。大まかなフローはこんな感じです。 事前に HTML と Android のコンポーネントの対応を決める HTML を Kotlin でも扱える形式に変換する HTML の要素を探索する マッチした要素を Android のコンポーネントで置き換える それぞれのフローについて解説していきます。 1. 事前に HTML と Android のコンポーネントの対応表を作る 簡単にですが下表のような対応を決めます。(検証段階だったので、いくつかの要素は省略しています) 2. HTML を Kotlin で扱える形式に変換する HTML を生の String として操作するのは流石に辛いので、Kotlin でも扱いやすいような形に変換します。 今回は  jhy/jsoup  という便利そうなライブラリを見つけたため、これを利用することにしました。 jsoup は Java 製の HTML パーサーで  example  にもあるように HTML を Kotlin でも扱いやすい形式に変換することができます。 下記の例は Wikipedia Root Node から小要素を探索していく様子です。 HTML タグの情報・親要素・内容のテキストを利用したいため保持しておきます。 val doc = Jsoup. connect ( "https://en.wikipedia.org/" ). get () doc. childNode ( 1 ). childNode ( 2 ). childNode ( 1 ) /* {Element@5739} "<div id="mw-page-base" class="noprint"></div>" attributes = {Attributes@5755} baseUri = "https://en.wikipedia.org/wiki/Main_Page" childNodes = {Collections$EmptyList@5720} size = 0 shadowChildrenRef = null tag = {Tag@5756} canContainInline = true empty = false formList = false formSubmit = false formatAsBlock = true isBlock = true preserveWhitespace = false selfClosing = false tagName = "div" parentNode = {Element@5729} siblingIndex = 1 */ 3. HTML の要素を探索する HTML の要素を探索する 再帰を利用して Root Node から小要素を探索し、jsoup で置き換えていきます。 class HTMLConverter ( private val html: String ) { fun parse () { val body = Jsoup. parse (html). normalise (). body () inspect (body, ElementViewHolder ()) } private fun inspect (parent: Element ) { parent. children (). forEach { if (parent.children. isEmpty ()) { return @forEach } inspect (it) } } 4. マッチした要素を Android のコンポーネントで置き換える 事前に定義した方法に従って、HTML をそれぞれ対応する Android コンポーネントに変換していきます。 Img 要素の場合: Glide を利用して画像を非同期で取得し ImageView にラップする。 private fun convertImage (el: Element ): View ? { val url = HttpUrl. parse (el. absUrl ( "src" )) ?: return null return ImageView (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) val header = LazyHeaders. Builder (). addHeader ( "Content-Type" , "image/bmp" ). build () val glideUrl = GlideUrl (url. url (), header) Glide. with (context) . asBitmap () . load (glideUrl) . into ( this ) } Table 要素の場合: Table と TableRow をそれぞれ作成して合成する。 private fun parseTable (el: Element ): View ? { return TableLayout (context). apply { layoutParams = LinearLayout. LayoutParams (MATCH_PARENT, WRAP_CONTENT) } } private fun createTableRow (): View ? { return TableRow (context) } private fun composeTableRow (self: TableRow , parent: TableLayout ): Boolean { table. addView (self) return true } Android Emulator で表示してみる 上記の方法で作成したモックがこちらになります。 ただ HTML を表示するだけであれば WebView を利用すればもっと楽に表示することもできますが、UI をカスタマイズしたい場合にはデータに変更を加えなければいけません。 今回のようなアプローチをとることで、もともとある HTML のデータを壊すことなく Android に適したデザインを実現しやすくなったのではないでしょうか。 さいごに コミュ障故に人前で話すことを避けてきたのですが、 TechLunch という機会をいただき Android なネタを発表をさせていただきました。 今回作成したモックは技術検証目的であるためリリースの予定はありませんが、発表のために書いた実装例は  seka/HTMLConverter.kt  として公開しています。 このようなアプローチを取る機会は少ないと思いますが、読んでくださった方の力になれれば幸いです!
こんにちは、開発本部の宮内です。先日のメドレーの社内勉強会「TechLunch」で、「とりまわかる TTS」と題して Web Speech API のお話をしました。 Web Speech API とは? macOS に、 say というコマンドがあるのはご存知でしょうか? このコマンドは引数で受け取った文字列を発音してくれるというコマンドです。 ターミナルアプリを開いて、次のようなコマンドを入力してみてください。 say -v Kyoko "ご用件は何でしょう?" このようにテキストから人間の声のように発音させる仕組みを 音声合成 といいます。 このような音声合成や 音声認識 に関しては macOS 以外にも Amazon Transcribe や Google Cloud Speech API などのクラウドサービスや Android では TextToSpeech クラス という API が用意されていたりもします。 今回の発表で使った Web Speech API は、ブラウザでこれらの音声合成・認識を行うための API 仕様です。 実際に試してみる 音声合成の サンプルページ を作りましたので、サンプルプログラムを抜粋して使い方の説明をしていきます。 voiceschanged イベント内で利用可能な音声を取得する constspeechSynthesis = window . speechSynthesis ; speechSynthesis . addEventListener ( "voiceschanged" , () => { buildVoiceOption ( $voices , speechSynthesis . getVoices ()); }); window.speechSynthesis.getVoices 関数を使うと利用可能な音声の一覧が取得できます。 ただし、ページロード直後ですと、タイミングによっては空配列が帰ってくることがあります。 そのため、 voiceschanged イベントを受け取ってから、 window.speechSynthesis.getVoices 関数を呼び出すことによって、確実に実行できるようにしています。 返却されてくる voice は仕様としてはユーザエージェントとブラウザの場合だとローカルに用意されている音声の種類で決ってくることになっています。 macOS の日本語であれば以下のような種類の voice が返ってきます。 buildVoiceOption は利用する音声を select タグで選択できるように設定する関数なので割愛します。 SpeechSynthesisUtterance で音声の設定をしていく SpeechSynthesisUtterance とは以下のような働きをする API になります。 Web Speech API の SpeechSynthesisUtterance インターフェイスは、音声要求を表現します。 これには、音声サービスが読むべき内容とその読み方に関する情報(例えば、言語、ピッチ、音量など)が含まれています。 https://developer.mozilla.org/ja/docs/Web/API/SpeechSynthesisUtterance SpeechSynthesis を使って実際に音声を発音させるにはこの API を使ってどのように発音させるのか?を設定していく必要があります。 function setVoice ( utterance , voices , voiceName ) { constchoices = voices . filter (( voice ) => voice . name === voiceName ); if ( choices . length > 0 ) { utterance . voice = choices [ 0 ]; } else { const defaultVoice = voices . find (( voice ) => voice . default ); if ( defaultVoice ) { utterance . voice = defaultVoice ; } } } $form . addEventListener ( "submit" , ( event ) => { event . preventDefault (); const utterance = new SpeechSynthesisUtterance ( $textarea . value ); setVoice ( utterance , speechSynthesis . getVoices (), $voices . selectedOptions [ 0 ]. dataset . name ); utterance . pitch = $pitch . valueAsNumber ; utterance . rate = $rate . valueAsNumber ; utterance . volume = $volume . valueAsNumber ; speechSynthesis . speak ( utterance ); }); フォームがサブミットされたら SpeechSynthesisUtterance クラスのインスタンスを作成し、 それを引数に speechSynthesis.speak 関数を呼び出せば、テキストボックスに入力されたテキストと SpeechSynthesisUtterance で設定したピッチ、音量などを元に設定された音声が出力されるようにしています。 (サンプルプログラムではエラー処理を省いているため、ピッチや音量の調整スライダーで極端な値を指定すると、正しく音声が出力されないことがあります) 今回の記事では紹介していませんが、Web Speech API にはもうひとつ SpeechRecognition API があり、こちらは音声認識をブラウザでできるようにする API になっています。興味があればぜひ調べてみてください。 まとめ 今回は Web Speech API の音声合成のさわりを紹介しましたが、ご覧のとおりとても簡単に使うことができます。 例えば、ブラウザの内容の読み上げをしてアクセシビリティを高めたりなどの使い方や、読みが難しい専門用語の発音を聞かせるようにするなど色々な使いかたが考えられるのではないでしょうか? この記事をご覧のみなさんもちょっと忘れかけられた、この API を使ってみてはいかがでしょう?
こんにちは、開発本部の宮内です。先日のメドレーの社内勉強会「TechLunch」で、「とりまわかる TTS」と題して Web Speech API のお話をしました。 Web Speech API とは? macOS に、 say というコマンドがあるのはご存知でしょうか? このコマンドは引数で受け取った文字列を発音してくれるというコマンドです。 ターミナルアプリを開いて、次のようなコマンドを入力してみてください。 say -v Kyoko "ご用件は何でしょう?" このようにテキストから人間の声のように発音させる仕組みを 音声合成 といいます。 このような音声合成や 音声認識 に関しては macOS 以外にも Amazon Transcribe や Google Cloud Speech API などのクラウドサービスや Android では TextToSpeech クラス という API が用意されていたりもします。 今回の発表で使った Web Speech API は、ブラウザでこれらの音声合成・認識を行うための API 仕様です。 実際に試してみる 音声合成の サンプルページ を作りましたので、サンプルプログラムを抜粋して使い方の説明をしていきます。 voiceschanged イベント内で利用可能な音声を取得する constspeechSynthesis = window . speechSynthesis ; speechSynthesis . addEventListener ( "voiceschanged" , () => { buildVoiceOption ( $voices , speechSynthesis . getVoices ()); }); window.speechSynthesis.getVoices 関数を使うと利用可能な音声の一覧が取得できます。 ただし、ページロード直後ですと、タイミングによっては空配列が帰ってくることがあります。 そのため、 voiceschanged イベントを受け取ってから、 window.speechSynthesis.getVoices 関数を呼び出すことによって、確実に実行できるようにしています。 返却されてくる voice は仕様としてはユーザエージェントとブラウザの場合だとローカルに用意されている音声の種類で決ってくることになっています。 macOS の日本語であれば以下のような種類の voice が返ってきます。 buildVoiceOption は利用する音声を select タグで選択できるように設定する関数なので割愛します。 SpeechSynthesisUtterance で音声の設定をしていく SpeechSynthesisUtterance とは以下のような働きをする API になります。 Web Speech API の SpeechSynthesisUtterance インターフェイスは、音声要求を表現します。 これには、音声サービスが読むべき内容とその読み方に関する情報(例えば、言語、ピッチ、音量など)が含まれています。 https://developer.mozilla.org/ja/docs/Web/API/SpeechSynthesisUtterance SpeechSynthesis を使って実際に音声を発音させるにはこの API を使ってどのように発音させるのか?を設定していく必要があります。 function setVoice ( utterance , voices , voiceName ) { constchoices = voices . filter (( voice ) => voice . name === voiceName ); if ( choices . length > 0 ) { utterance . voice = choices [ 0 ]; } else { const defaultVoice = voices . find (( voice ) => voice . default ); if ( defaultVoice ) { utterance . voice = defaultVoice ; } } } $form . addEventListener ( "submit" , ( event ) => { event . preventDefault (); const utterance = new SpeechSynthesisUtterance ( $textarea . value ); setVoice ( utterance , speechSynthesis . getVoices (), $voices . selectedOptions [ 0 ]. dataset . name ); utterance . pitch = $pitch . valueAsNumber ; utterance . rate = $rate . valueAsNumber ; utterance . volume = $volume . valueAsNumber ; speechSynthesis . speak ( utterance ); }); フォームがサブミットされたら SpeechSynthesisUtterance クラスのインスタンスを作成し、 それを引数に speechSynthesis.speak 関数を呼び出せば、テキストボックスに入力されたテキストと SpeechSynthesisUtterance で設定したピッチ、音量などを元に設定された音声が出力されるようにしています。 (サンプルプログラムではエラー処理を省いているため、ピッチや音量の調整スライダーで極端な値を指定すると、正しく音声が出力されないことがあります) 今回の記事では紹介していませんが、Web Speech API にはもうひとつ SpeechRecognition API があり、こちらは音声認識をブラウザでできるようにする API になっています。興味があればぜひ調べてみてください。 まとめ 今回は Web Speech API の音声合成のさわりを紹介しましたが、ご覧のとおりとても簡単に使うことができます。 例えば、ブラウザの内容の読み上げをしてアクセシビリティを高めたりなどの使い方や、読みが難しい専門用語の発音を聞かせるようにするなど色々な使いかたが考えられるのではないでしょうか? この記事をご覧のみなさんもちょっと忘れかけられた、この API を使ってみてはいかがでしょう?
こんにちは、開発本部の宮内です。先日のメドレーの社内勉強会「TechLunch」で、「とりまわかる TTS」と題して Web Speech API のお話をしました。 Web Speech API とは? macOS に、 say というコマンドがあるのはご存知でしょうか? このコマンドは引数で受け取った文字列を発音してくれるというコマンドです。 ターミナルアプリを開いて、次のようなコマンドを入力してみてください。 say -v Kyoko "ご用件は何でしょう?" このようにテキストから人間の声のように発音させる仕組みを 音声合成 といいます。 このような音声合成や 音声認識 に関しては macOS 以外にも Amazon Transcribe や Google Cloud Speech API などのクラウドサービスや Android では TextToSpeech クラス という API が用意されていたりもします。 今回の発表で使った Web Speech API は、ブラウザでこれらの音声合成・認識を行うための API 仕様です。 実際に試してみる 音声合成の サンプルページ を作りましたので、サンプルプログラムを抜粋して使い方の説明をしていきます。 voiceschanged イベント内で利用可能な音声を取得する constspeechSynthesis = window . speechSynthesis ; speechSynthesis . addEventListener ( "voiceschanged" , () => { buildVoiceOption ( $voices , speechSynthesis . getVoices ()); }); window.speechSynthesis.getVoices 関数を使うと利用可能な音声の一覧が取得できます。 ただし、ページロード直後ですと、タイミングによっては空配列が帰ってくることがあります。 そのため、 voiceschanged イベントを受け取ってから、 window.speechSynthesis.getVoices 関数を呼び出すことによって、確実に実行できるようにしています。 返却されてくる voice は仕様としてはユーザエージェントとブラウザの場合だとローカルに用意されている音声の種類で決ってくることになっています。 macOS の日本語であれば以下のような種類の voice が返ってきます。 buildVoiceOption は利用する音声を select タグで選択できるように設定する関数なので割愛します。 SpeechSynthesisUtterance で音声の設定をしていく SpeechSynthesisUtterance とは以下のような働きをする API になります。 Web Speech API の SpeechSynthesisUtterance インターフェイスは、音声要求を表現します。 これには、音声サービスが読むべき内容とその読み方に関する情報(例えば、言語、ピッチ、音量など)が含まれています。 https://developer.mozilla.org/ja/docs/Web/API/SpeechSynthesisUtterance SpeechSynthesis を使って実際に音声を発音させるにはこの API を使ってどのように発音させるのか?を設定していく必要があります。 function setVoice ( utterance , voices , voiceName ) { constchoices = voices . filter (( voice ) => voice . name === voiceName ); if ( choices . length > 0 ) { utterance . voice = choices [ 0 ]; } else { const defaultVoice = voices . find (( voice ) => voice . default ); if ( defaultVoice ) { utterance . voice = defaultVoice ; } } } $form . addEventListener ( "submit" , ( event ) => { event . preventDefault (); const utterance = new SpeechSynthesisUtterance ( $textarea . value ); setVoice ( utterance , speechSynthesis . getVoices (), $voices . selectedOptions [ 0 ]. dataset . name ); utterance . pitch = $pitch . valueAsNumber ; utterance . rate = $rate . valueAsNumber ; utterance . volume = $volume . valueAsNumber ; speechSynthesis . speak ( utterance ); }); フォームがサブミットされたら SpeechSynthesisUtterance クラスのインスタンスを作成し、 それを引数に speechSynthesis.speak 関数を呼び出せば、テキストボックスに入力されたテキストと SpeechSynthesisUtterance で設定したピッチ、音量などを元に設定された音声が出力されるようにしています。 (サンプルプログラムではエラー処理を省いているため、ピッチや音量の調整スライダーで極端な値を指定すると、正しく音声が出力されないことがあります) 今回の記事では紹介していませんが、Web Speech API にはもうひとつ SpeechRecognition API があり、こちらは音声認識をブラウザでできるようにする API になっています。興味があればぜひ調べてみてください。 まとめ 今回は Web Speech API の音声合成のさわりを紹介しましたが、ご覧のとおりとても簡単に使うことができます。 例えば、ブラウザの内容の読み上げをしてアクセシビリティを高めたりなどの使い方や、読みが難しい専門用語の発音を聞かせるようにするなど色々な使いかたが考えられるのではないでしょうか? この記事をご覧のみなさんもちょっと忘れかけられた、この API を使ってみてはいかがでしょう?
こんにちは、開発本部の宮内です。先日のメドレーの社内勉強会「TechLunch」で、「とりまわかる TTS」と題して Web Speech API のお話をしました。 Web Speech API とは? macOS に、 say というコマンドがあるのはご存知でしょうか? このコマンドは引数で受け取った文字列を発音してくれるというコマンドです。 ターミナルアプリを開いて、次のようなコマンドを入力してみてください。 say -v Kyoko "ご用件は何でしょう?" このようにテキストから人間の声のように発音させる仕組みを 音声合成 といいます。 このような音声合成や 音声認識 に関しては macOS 以外にも Amazon Transcribe や Google Cloud Speech API などのクラウドサービスや Android では TextToSpeech クラス という API が用意されていたりもします。 今回の発表で使った Web Speech API は、ブラウザでこれらの音声合成・認識を行うための API 仕様です。 実際に試してみる 音声合成の サンプルページ を作りましたので、サンプルプログラムを抜粋して使い方の説明をしていきます。 voiceschanged イベント内で利用可能な音声を取得する constspeechSynthesis = window . speechSynthesis ; speechSynthesis . addEventListener ( "voiceschanged" , () => { buildVoiceOption ( $voices , speechSynthesis . getVoices ()); }); window.speechSynthesis.getVoices 関数を使うと利用可能な音声の一覧が取得できます。 ただし、ページロード直後ですと、タイミングによっては空配列が帰ってくることがあります。 そのため、 voiceschanged イベントを受け取ってから、 window.speechSynthesis.getVoices 関数を呼び出すことによって、確実に実行できるようにしています。 返却されてくる voice は仕様としてはユーザエージェントとブラウザの場合だとローカルに用意されている音声の種類で決ってくることになっています。 macOS の日本語であれば以下のような種類の voice が返ってきます。 buildVoiceOption は利用する音声を select タグで選択できるように設定する関数なので割愛します。 SpeechSynthesisUtterance で音声の設定をしていく SpeechSynthesisUtterance とは以下のような働きをする API になります。 Web Speech API の SpeechSynthesisUtterance インターフェイスは、音声要求を表現します。 これには、音声サービスが読むべき内容とその読み方に関する情報(例えば、言語、ピッチ、音量など)が含まれています。 https://developer.mozilla.org/ja/docs/Web/API/SpeechSynthesisUtterance SpeechSynthesis を使って実際に音声を発音させるにはこの API を使ってどのように発音させるのか?を設定していく必要があります。 function setVoice ( utterance , voices , voiceName ) { constchoices = voices . filter (( voice ) => voice . name === voiceName ); if ( choices . length > 0 ) { utterance . voice = choices [ 0 ]; } else { const defaultVoice = voices . find (( voice ) => voice . default ); if ( defaultVoice ) { utterance . voice = defaultVoice ; } } } $form . addEventListener ( "submit" , ( event ) => { event . preventDefault (); const utterance = new SpeechSynthesisUtterance ( $textarea . value ); setVoice ( utterance , speechSynthesis . getVoices (), $voices . selectedOptions [ 0 ]. dataset . name ); utterance . pitch = $pitch . valueAsNumber ; utterance . rate = $rate . valueAsNumber ; utterance . volume = $volume . valueAsNumber ; speechSynthesis . speak ( utterance ); }); フォームがサブミットされたら SpeechSynthesisUtterance クラスのインスタンスを作成し、 それを引数に speechSynthesis.speak 関数を呼び出せば、テキストボックスに入力されたテキストと SpeechSynthesisUtterance で設定したピッチ、音量などを元に設定された音声が出力されるようにしています。 (サンプルプログラムではエラー処理を省いているため、ピッチや音量の調整スライダーで極端な値を指定すると、正しく音声が出力されないことがあります) 今回の記事では紹介していませんが、Web Speech API にはもうひとつ SpeechRecognition API があり、こちらは音声認識をブラウザでできるようにする API になっています。興味があればぜひ調べてみてください。 まとめ 今回は Web Speech API の音声合成のさわりを紹介しましたが、ご覧のとおりとても簡単に使うことができます。 例えば、ブラウザの内容の読み上げをしてアクセシビリティを高めたりなどの使い方や、読みが難しい専門用語の発音を聞かせるようにするなど色々な使いかたが考えられるのではないでしょうか? この記事をご覧のみなさんもちょっと忘れかけられた、この API を使ってみてはいかがでしょう?
こんにちは、開発本部の宮内です。先日のメドレーの社内勉強会「TechLunch」で、「とりまわかる TTS」と題して Web Speech API のお話をしました。 Web Speech API とは? macOS に、 say というコマンドがあるのはご存知でしょうか? このコマンドは引数で受け取った文字列を発音してくれるというコマンドです。 ターミナルアプリを開いて、次のようなコマンドを入力してみてください。 say -v Kyoko "ご用件は何でしょう?" このようにテキストから人間の声のように発音させる仕組みを 音声合成 といいます。 このような音声合成や 音声認識 に関しては macOS 以外にも Amazon Transcribe や Google Cloud Speech API などのクラウドサービスや Android では TextToSpeech クラス という API が用意されていたりもします。 今回の発表で使った Web Speech API は、ブラウザでこれらの音声合成・認識を行うための API 仕様です。 実際に試してみる 音声合成の サンプルページ を作りましたので、サンプルプログラムを抜粋して使い方の説明をしていきます。 voiceschanged イベント内で利用可能な音声を取得する constspeechSynthesis = window . speechSynthesis ; speechSynthesis . addEventListener ( "voiceschanged" , () => { buildVoiceOption ( $voices , speechSynthesis . getVoices ()); }); window.speechSynthesis.getVoices 関数を使うと利用可能な音声の一覧が取得できます。 ただし、ページロード直後ですと、タイミングによっては空配列が帰ってくることがあります。 そのため、 voiceschanged イベントを受け取ってから、 window.speechSynthesis.getVoices 関数を呼び出すことによって、確実に実行できるようにしています。 返却されてくる voice は仕様としてはユーザエージェントとブラウザの場合だとローカルに用意されている音声の種類で決ってくることになっています。 macOS の日本語であれば以下のような種類の voice が返ってきます。 buildVoiceOption は利用する音声を select タグで選択できるように設定する関数なので割愛します。 SpeechSynthesisUtterance で音声の設定をしていく SpeechSynthesisUtterance とは以下のような働きをする API になります。 Web Speech API の SpeechSynthesisUtterance インターフェイスは、音声要求を表現します。 これには、音声サービスが読むべき内容とその読み方に関する情報(例えば、言語、ピッチ、音量など)が含まれています。 https://developer.mozilla.org/ja/docs/Web/API/SpeechSynthesisUtterance SpeechSynthesis を使って実際に音声を発音させるにはこの API を使ってどのように発音させるのか?を設定していく必要があります。 function setVoice ( utterance , voices , voiceName ) { constchoices = voices . filter (( voice ) => voice . name === voiceName ); if ( choices . length > 0 ) { utterance . voice = choices [ 0 ]; } else { const defaultVoice = voices . find (( voice ) => voice . default ); if ( defaultVoice ) { utterance . voice = defaultVoice ; } } } $form . addEventListener ( "submit" , ( event ) => { event . preventDefault (); const utterance = new SpeechSynthesisUtterance ( $textarea . value ); setVoice ( utterance , speechSynthesis . getVoices (), $voices . selectedOptions [ 0 ]. dataset . name ); utterance . pitch = $pitch . valueAsNumber ; utterance . rate = $rate . valueAsNumber ; utterance . volume = $volume . valueAsNumber ; speechSynthesis . speak ( utterance ); }); フォームがサブミットされたら SpeechSynthesisUtterance クラスのインスタンスを作成し、 それを引数に speechSynthesis.speak 関数を呼び出せば、テキストボックスに入力されたテキストと SpeechSynthesisUtterance で設定したピッチ、音量などを元に設定された音声が出力されるようにしています。 (サンプルプログラムではエラー処理を省いているため、ピッチや音量の調整スライダーで極端な値を指定すると、正しく音声が出力されないことがあります) 今回の記事では紹介していませんが、Web Speech API にはもうひとつ SpeechRecognition API があり、こちらは音声認識をブラウザでできるようにする API になっています。興味があればぜひ調べてみてください。 まとめ 今回は Web Speech API の音声合成のさわりを紹介しましたが、ご覧のとおりとても簡単に使うことができます。 例えば、ブラウザの内容の読み上げをしてアクセシビリティを高めたりなどの使い方や、読みが難しい専門用語の発音を聞かせるようにするなど色々な使いかたが考えられるのではないでしょうか? この記事をご覧のみなさんもちょっと忘れかけられた、この API を使ってみてはいかがでしょう?
こんにちは、開発本部の宮内です。先日のメドレーの社内勉強会「TechLunch」で、「とりまわかる TTS」と題して Web Speech API のお話をしました。 Web Speech API とは? macOS に、 say というコマンドがあるのはご存知でしょうか? このコマンドは引数で受け取った文字列を発音してくれるというコマンドです。 ターミナルアプリを開いて、次のようなコマンドを入力してみてください。 say -v Kyoko "ご用件は何でしょう?" このようにテキストから人間の声のように発音させる仕組みを 音声合成 といいます。 このような音声合成や 音声認識 に関しては macOS 以外にも Amazon Transcribe や Google Cloud Speech API などのクラウドサービスや Android では TextToSpeech クラス という API が用意されていたりもします。 今回の発表で使った Web Speech API は、ブラウザでこれらの音声合成・認識を行うための API 仕様です。 実際に試してみる 音声合成の サンプルページ を作りましたので、サンプルプログラムを抜粋して使い方の説明をしていきます。 voiceschanged イベント内で利用可能な音声を取得する constspeechSynthesis = window . speechSynthesis ; speechSynthesis . addEventListener ( "voiceschanged" , () => { buildVoiceOption ( $voices , speechSynthesis . getVoices ()); }); window.speechSynthesis.getVoices 関数を使うと利用可能な音声の一覧が取得できます。 ただし、ページロード直後ですと、タイミングによっては空配列が帰ってくることがあります。 そのため、 voiceschanged イベントを受け取ってから、 window.speechSynthesis.getVoices 関数を呼び出すことによって、確実に実行できるようにしています。 返却されてくる voice は仕様としてはユーザエージェントとブラウザの場合だとローカルに用意されている音声の種類で決ってくることになっています。 macOS の日本語であれば以下のような種類の voice が返ってきます。 buildVoiceOption は利用する音声を select タグで選択できるように設定する関数なので割愛します。 SpeechSynthesisUtterance で音声の設定をしていく SpeechSynthesisUtterance とは以下のような働きをする API になります。 Web Speech API の SpeechSynthesisUtterance インターフェイスは、音声要求を表現します。 これには、音声サービスが読むべき内容とその読み方に関する情報(例えば、言語、ピッチ、音量など)が含まれています。 https://developer.mozilla.org/ja/docs/Web/API/SpeechSynthesisUtterance SpeechSynthesis を使って実際に音声を発音させるにはこの API を使ってどのように発音させるのか?を設定していく必要があります。 function setVoice ( utterance , voices , voiceName ) { constchoices = voices . filter (( voice ) => voice . name === voiceName ); if ( choices . length > 0 ) { utterance . voice = choices [ 0 ]; } else { const defaultVoice = voices . find (( voice ) => voice . default ); if ( defaultVoice ) { utterance . voice = defaultVoice ; } } } $form . addEventListener ( "submit" , ( event ) => { event . preventDefault (); const utterance = new SpeechSynthesisUtterance ( $textarea . value ); setVoice ( utterance , speechSynthesis . getVoices (), $voices . selectedOptions [ 0 ]. dataset . name ); utterance . pitch = $pitch . valueAsNumber ; utterance . rate = $rate . valueAsNumber ; utterance . volume = $volume . valueAsNumber ; speechSynthesis . speak ( utterance ); }); フォームがサブミットされたら SpeechSynthesisUtterance クラスのインスタンスを作成し、 それを引数に speechSynthesis.speak 関数を呼び出せば、テキストボックスに入力されたテキストと SpeechSynthesisUtterance で設定したピッチ、音量などを元に設定された音声が出力されるようにしています。 (サンプルプログラムではエラー処理を省いているため、ピッチや音量の調整スライダーで極端な値を指定すると、正しく音声が出力されないことがあります) 今回の記事では紹介していませんが、Web Speech API にはもうひとつ SpeechRecognition API があり、こちらは音声認識をブラウザでできるようにする API になっています。興味があればぜひ調べてみてください。 まとめ 今回は Web Speech API の音声合成のさわりを紹介しましたが、ご覧のとおりとても簡単に使うことができます。 例えば、ブラウザの内容の読み上げをしてアクセシビリティを高めたりなどの使い方や、読みが難しい専門用語の発音を聞かせるようにするなど色々な使いかたが考えられるのではないでしょうか? この記事をご覧のみなさんもちょっと忘れかけられた、この API を使ってみてはいかがでしょう?
こんにちは、開発本部の宮内です。先日のメドレーの社内勉強会「TechLunch」で、「とりまわかる TTS」と題して Web Speech API のお話をしました。 Web Speech API とは? macOS に、 say というコマンドがあるのはご存知でしょうか? このコマンドは引数で受け取った文字列を発音してくれるというコマンドです。 ターミナルアプリを開いて、次のようなコマンドを入力してみてください。 say -v Kyoko "ご用件は何でしょう?" このようにテキストから人間の声のように発音させる仕組みを 音声合成 といいます。 このような音声合成や 音声認識 に関しては macOS 以外にも Amazon Transcribe や Google Cloud Speech API などのクラウドサービスや Android では TextToSpeech クラス という API が用意されていたりもします。 今回の発表で使った Web Speech API は、ブラウザでこれらの音声合成・認識を行うための API 仕様です。 実際に試してみる 音声合成の サンプルページ を作りましたので、サンプルプログラムを抜粋して使い方の説明をしていきます。 voiceschanged イベント内で利用可能な音声を取得する constspeechSynthesis = window . speechSynthesis ; speechSynthesis . addEventListener ( "voiceschanged" , () => { buildVoiceOption ( $voices , speechSynthesis . getVoices ()); }); window.speechSynthesis.getVoices 関数を使うと利用可能な音声の一覧が取得できます。 ただし、ページロード直後ですと、タイミングによっては空配列が帰ってくることがあります。 そのため、 voiceschanged イベントを受け取ってから、 window.speechSynthesis.getVoices 関数を呼び出すことによって、確実に実行できるようにしています。 返却されてくる voice は仕様としてはユーザエージェントとブラウザの場合だとローカルに用意されている音声の種類で決ってくることになっています。 macOS の日本語であれば以下のような種類の voice が返ってきます。 buildVoiceOption は利用する音声を select タグで選択できるように設定する関数なので割愛します。 SpeechSynthesisUtterance で音声の設定をしていく SpeechSynthesisUtterance とは以下のような働きをする API になります。 Web Speech API の SpeechSynthesisUtterance インターフェイスは、音声要求を表現します。 これには、音声サービスが読むべき内容とその読み方に関する情報(例えば、言語、ピッチ、音量など)が含まれています。 https://developer.mozilla.org/ja/docs/Web/API/SpeechSynthesisUtterance SpeechSynthesis を使って実際に音声を発音させるにはこの API を使ってどのように発音させるのか?を設定していく必要があります。 function setVoice ( utterance , voices , voiceName ) { constchoices = voices . filter (( voice ) => voice . name === voiceName ); if ( choices . length > 0 ) { utterance . voice = choices [ 0 ]; } else { const defaultVoice = voices . find (( voice ) => voice . default ); if ( defaultVoice ) { utterance . voice = defaultVoice ; } } } $form . addEventListener ( "submit" , ( event ) => { event . preventDefault (); const utterance = new SpeechSynthesisUtterance ( $textarea . value ); setVoice ( utterance , speechSynthesis . getVoices (), $voices . selectedOptions [ 0 ]. dataset . name ); utterance . pitch = $pitch . valueAsNumber ; utterance . rate = $rate . valueAsNumber ; utterance . volume = $volume . valueAsNumber ; speechSynthesis . speak ( utterance ); }); フォームがサブミットされたら SpeechSynthesisUtterance クラスのインスタンスを作成し、 それを引数に speechSynthesis.speak 関数を呼び出せば、テキストボックスに入力されたテキストと SpeechSynthesisUtterance で設定したピッチ、音量などを元に設定された音声が出力されるようにしています。 (サンプルプログラムではエラー処理を省いているため、ピッチや音量の調整スライダーで極端な値を指定すると、正しく音声が出力されないことがあります) 今回の記事では紹介していませんが、Web Speech API にはもうひとつ SpeechRecognition API があり、こちらは音声認識をブラウザでできるようにする API になっています。興味があればぜひ調べてみてください。 まとめ 今回は Web Speech API の音声合成のさわりを紹介しましたが、ご覧のとおりとても簡単に使うことができます。 例えば、ブラウザの内容の読み上げをしてアクセシビリティを高めたりなどの使い方や、読みが難しい専門用語の発音を聞かせるようにするなど色々な使いかたが考えられるのではないでしょうか? この記事をご覧のみなさんもちょっと忘れかけられた、この API を使ってみてはいかがでしょう?
こんにちは、開発本部の宮内です。先日のメドレーの社内勉強会「TechLunch」で、「とりまわかる TTS」と題して Web Speech API のお話をしました。 Web Speech API とは? macOS に、 say というコマンドがあるのはご存知でしょうか? このコマンドは引数で受け取った文字列を発音してくれるというコマンドです。 ターミナルアプリを開いて、次のようなコマンドを入力してみてください。 say -v Kyoko "ご用件は何でしょう?" このようにテキストから人間の声のように発音させる仕組みを 音声合成 といいます。 このような音声合成や 音声認識 に関しては macOS 以外にも Amazon Transcribe や Google Cloud Speech API などのクラウドサービスや Android では TextToSpeech クラス という API が用意されていたりもします。 今回の発表で使った Web Speech API は、ブラウザでこれらの音声合成・認識を行うための API 仕様です。 実際に試してみる 音声合成の サンプルページ を作りましたので、サンプルプログラムを抜粋して使い方の説明をしていきます。 voiceschanged イベント内で利用可能な音声を取得する constspeechSynthesis = window . speechSynthesis ; speechSynthesis . addEventListener ( "voiceschanged" , () => { buildVoiceOption ( $voices , speechSynthesis . getVoices ()); }); window.speechSynthesis.getVoices 関数を使うと利用可能な音声の一覧が取得できます。 ただし、ページロード直後ですと、タイミングによっては空配列が帰ってくることがあります。 そのため、 voiceschanged イベントを受け取ってから、 window.speechSynthesis.getVoices 関数を呼び出すことによって、確実に実行できるようにしています。 返却されてくる voice は仕様としてはユーザエージェントとブラウザの場合だとローカルに用意されている音声の種類で決ってくることになっています。 macOS の日本語であれば以下のような種類の voice が返ってきます。 buildVoiceOption は利用する音声を select タグで選択できるように設定する関数なので割愛します。 SpeechSynthesisUtterance で音声の設定をしていく SpeechSynthesisUtterance とは以下のような働きをする API になります。 Web Speech API の SpeechSynthesisUtterance インターフェイスは、音声要求を表現します。 これには、音声サービスが読むべき内容とその読み方に関する情報(例えば、言語、ピッチ、音量など)が含まれています。 https://developer.mozilla.org/ja/docs/Web/API/SpeechSynthesisUtterance SpeechSynthesis を使って実際に音声を発音させるにはこの API を使ってどのように発音させるのか?を設定していく必要があります。 function setVoice ( utterance , voices , voiceName ) { constchoices = voices . filter (( voice ) => voice . name === voiceName ); if ( choices . length > 0 ) { utterance . voice = choices [ 0 ]; } else { const defaultVoice = voices . find (( voice ) => voice . default ); if ( defaultVoice ) { utterance . voice = defaultVoice ; } } } $form . addEventListener ( "submit" , ( event ) => { event . preventDefault (); const utterance = new SpeechSynthesisUtterance ( $textarea . value ); setVoice ( utterance , speechSynthesis . getVoices (), $voices . selectedOptions [ 0 ]. dataset . name ); utterance . pitch = $pitch . valueAsNumber ; utterance . rate = $rate . valueAsNumber ; utterance . volume = $volume . valueAsNumber ; speechSynthesis . speak ( utterance ); }); フォームがサブミットされたら SpeechSynthesisUtterance クラスのインスタンスを作成し、 それを引数に speechSynthesis.speak 関数を呼び出せば、テキストボックスに入力されたテキストと SpeechSynthesisUtterance で設定したピッチ、音量などを元に設定された音声が出力されるようにしています。 (サンプルプログラムではエラー処理を省いているため、ピッチや音量の調整スライダーで極端な値を指定すると、正しく音声が出力されないことがあります) 今回の記事では紹介していませんが、Web Speech API にはもうひとつ SpeechRecognition API があり、こちらは音声認識をブラウザでできるようにする API になっています。興味があればぜひ調べてみてください。 まとめ 今回は Web Speech API の音声合成のさわりを紹介しましたが、ご覧のとおりとても簡単に使うことができます。 例えば、ブラウザの内容の読み上げをしてアクセシビリティを高めたりなどの使い方や、読みが難しい専門用語の発音を聞かせるようにするなど色々な使いかたが考えられるのではないでしょうか? この記事をご覧のみなさんもちょっと忘れかけられた、この API を使ってみてはいかがでしょう?