【Android Studio】Googleサジェストで予測変換を取得【日本語】

Android Studio(Java)でGoogleサジェスト(google suggest)を使って入力した文字の検索予測を取得してみた。

やりたいこととは異なる結果だったのだが、これはこれで使えそうな知識だったので備忘録として記載。

 

他のプログラミングに関する記事はこちら

スポンサーリンク


 

【前提】

プロジェクトを作成した状態から、Layoutのxmlは以下の通り更新しているものとします。

内容としては最初から用意されているHello WorldのTextViewをEditText(idはtext1)に変更した感じです。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <EditText
        android:id="@+id/text1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text=""
        android:textSize="50sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

 

【早速実装してみる】

細かい話は置いておいて、まずはAndroidManifest.xmlを更新します。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yosokutest">
    
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.Yosokutest">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

追加したのは5行目の以下の記述のみ。

    <uses-permission android:name="android.permission.INTERNET"/>

Googleサジェストを使うためにはHTTP通信をする必要がありますが、マニフェストファイルに対し上記コードを設定することで、AndroidアプリでもHTTP通信が出来るようになります。

 

次に実際の処理のコード。

とりあえず全コードは以下の通り。

入力した文字に応じて、結果がXML形式でログに出力されます。

package com.example.yosokutest;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.widget.EditText;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MainActivity extends AppCompatActivity implements TextWatcher {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        EditText editText = findViewById(R.id.text1);
        // リスナーを登録
        editText.addTextChangedListener(this);
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
    }

    @Override
    public void afterTextChanged(Editable s) {
        // テキスト変更後に変更されたテキストを取り出す
        String inputStr= s.toString();
        getSuggest(inputStr);
    }

    public void getSuggest(String s) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // マルチスレッドにしたい処理 ここから
                try{
                    String urlName = "http://google.com/complete/search?q=" + s + "&output=toolbar";
                    URL url = new URL(urlName);
                    HttpURLConnection con = (HttpURLConnection)url.openConnection();
                    BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));

                    String line;
                    final StringBuilder sb = new StringBuilder();
                    while ((line = br.readLine()) != null) {
                        sb.append(line).append("\n");
                    }
                    Log.d("結果", decode(sb.toString()));
                } catch (Exception e) {
                }
                // マルチスレッドにしたい処理 ここまで
            }
        }).start();
    }

    public static String decode(String str) {
        // 10進数用
        // Pattern pattern = Pattern.compile("&#(\\d+);|&#([\\da-fA-F]+);");
        // 16進数用
        Pattern pattern = Pattern.compile("&#x(\\d+);|&#x([\\da-fA-F]+);");
        Matcher matcher = pattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        Character buf;
        while(matcher.find()){
            if(matcher.group(1) != null){
                buf = new Character(
                        (char)Integer.parseInt(matcher.group(1)));
            }else{
                buf = new Character(
                        (char)Integer.parseInt(matcher.group(2), 16));
            }
            matcher.appendReplacement(sb, buf.toString());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

}

 

【さっくり解説】

処理に関するコードについて、ざっくり開設していきます。

まずは以下記述について。

    @Override
    public void afterTextChanged(Editable s) {
        // テキスト変更後に変更されたテキストを取り出す
        String inputStr= s.toString();
        getSuggest(inputStr);
    }

このafterTextChangedメソッドにより、入力した文字がgetSuggestメソッドに渡ります。

afterTextChangedメソッドについてはこちらを参照してください。

文字を入力した際に起動するメソッドです。

 

次にgetSuggestメソッドの内容です。

    public void getSuggest(String s) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                // マルチスレッドにしたい処理 ここから
                try{
                    String urlName = "http://google.com/complete/search?q=" + s + "&output=toolbar";
                    URL url = new URL(urlName);
                    HttpURLConnection con = (HttpURLConnection)url.openConnection();
                    BufferedReader br = new BufferedReader(new InputStreamReader(con.getInputStream()));

                    String line;
                    final StringBuilder sb = new StringBuilder();
                    while ((line = br.readLine()) != null) {
                        sb.append(line).append("\n");
                    }
                    Log.d("結果", decode(sb.toString()));
                } catch (Exception e) {
                }
                // マルチスレッドにしたい処理 ここまで
            }
        }).start();
    }

Googleサジェストは以下のようなURLにアクセスすることで、検索の予測変換内容をXML形式で返します。

http://google.com/complete/search?q=[検索したワード]&output=toolbar

 

また、AndroidアプリでHTTP通信をするためには、別スレッドでアクセスする必要があるのでrunでアクセスするようにしています。

その後の内容としては、引数で受け取った文字でURLを作成し、HTTP通信で結果を取得>取得した結果を文字列型にして、最終的にdecodeメソッドに投げています。

 

 

次にdecodeメソッドについて。

    public static String decode(String str) {
        // 10進数用
        // Pattern pattern = Pattern.compile("&#(\\d+);|&#([\\da-fA-F]+);");
        // 16進数用
        Pattern pattern = Pattern.compile("&#x(\\d+);|&#x([\\da-fA-F]+);");
        Matcher matcher = pattern.matcher(str);
        StringBuffer sb = new StringBuffer();
        Character buf;
        while(matcher.find()){
            if(matcher.group(1) != null){
                buf = new Character(
                        (char)Integer.parseInt(matcher.group(1)));
            }else{
                buf = new Character(
                        (char)Integer.parseInt(matcher.group(2), 16));
            }
            matcher.appendReplacement(sb, buf.toString());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

どこかから拾ってきたコードを少し改良しただけですが……。

HTTP通信で取得した文字列は、全て数値文字参照という形で表記されています。

そのため、数値文字参照から人間が読み取れる文字に戻すのがこのメソッドです。

 

尚、数値文字列とは、10進数の場合は「&#」から「;」の間にある数値で文字を表すもので、16進数の場合は「&#x」から「;」の間にある数値で文字を表すものです。

取得する環境によって10進数なのか16進数なのかが変わるようなので、適時3行目(10進数用のパターン)を使うか5行目(16進数用のパターン)を使うかを決めてください。

このメソッドの処理内容としては、パターンに一致するものは文字列にデコードして最終的な結果を返しているだけです。

 

ここまでくればXML形式で色々操作できるとおもいます。

 

他のプログラミングに関する記事はこちら

スポンサーリンク