Help us understand the problem. What is going on with this article?

[Android] コピーできないテキストをコピーする

Androidで選択できない(textIsSelectable=false)テキストをコピーする方法。
スクショだと取り回しづらいし長文だと地獄なので、見つけた方法をメモしておく。

結論から言えば、uiautomator dumpコマンドで表示しているテキストの全文を取得できる。
たとえ選択できる場合でも、PCでゴニョゴニョしたい場合は、「選択」→「共有」より便利。
確認はAndroid10で実施した。

Screenshot_1571657754.png

この状態でuiautomator dumpコマンドを実施した結果が以下(見やすいようにxmllintで整形している)。
resource-id="xyz.takeoverjp.jugemuviewer:id/jugemView"textの中身が、目的の文字列になっていることがわかる。

$ adb shell uiautomator dump
UI hierchary dumped to: /sdcard/window_dump.xml
$ adb shell cat /sdcard/window_dump.xml | xmllint --format -
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<hierarchy rotation="0">
  <node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][1080,2040]">
    <node index="0" text="" resource-id="" class="android.widget.LinearLayout" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][1080,2040]">
      <node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,60][1080,2040]">
        <node index="0" text="" resource-id="xyz.takeoverjp.jugemuviewer:id/decor_content_parent" class="android.view.ViewGroup" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,60][1080,2040]">
          <node index="0" text="" resource-id="xyz.takeoverjp.jugemuviewer:id/action_bar_container" class="android.widget.FrameLayout" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,60][1080,200]">
            <node index="0" text="" resource-id="xyz.takeoverjp.jugemuviewer:id/action_bar" class="android.view.ViewGroup" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,60][1080,200]">
              <node index="0" text="JugemuViewer" resource-id="" class="android.widget.TextView" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[40,96][377,163]"/>
            </node>
          </node>
          <node index="1" text="" resource-id="android:id/content" class="android.widget.FrameLayout" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,200][1080,2040]">
            <node index="0" text="" resource-id="" class="android.view.ViewGroup" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,200][1080,2040]">
              <node index="0" text="" resource-id="" class="android.widget.ScrollView" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="true" focused="false" scrollable="true" long-clickable="false" password="false" selected="false" bounds="[29,200][1052,2040]">
                <node index="0" text="寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助寿限無、寿限無、 五劫の擦り切れ、 海砂利水魚の、 水行末・雲来末・風来末、 喰う寝る処に住む処、 藪ら柑子の藪柑子、 パイポ・パイポ・パイポのシューリンガン、 シューリンガンのグーリンダイ、 グーリンダイのポンポコピーのポンポコナーの、 長久命の長助" resource-id="xyz.takeoverjp.jugemuviewer:id/jugemView" class="android.widget.TextView" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[29,200][1052,2040]"/>
              </node>
            </node>
          </node>
        </node>
      </node>
    </node>
    <node index="1" text="" resource-id="android:id/statusBarBackground" class="android.view.View" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][1080,60]"/>
    <node index="2" text="" resource-id="android:id/navigationBarBackground" class="android.view.View" package="xyz.takeoverjp.jugemuviewer" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" bounds="[0,0][0,0]"/>
  </node>
</hierarchy>

uiautomatorコマンドはUIを含む自動テスト用のフレームワークの一部。
テスト作成時のレイアウト調査用にdump機能を持っているので、利用した。

なお、Android Studioに付属するLayout Inspectorや、Android SDKに付属するuiautomatorviewerを使っても同じことができる。
GUIが好きな場合や、nodeと表示との関係がよくわからない場合はこれらを使えばいい。
TextViewmTextを選択してCtrl+Cすればコピー可能。

  • Layout Inspector

Layout Inspectorのスクリーンショット

  • uiautomatorviewer

uiautomatorviewerのスクリーンショット

参考

Test UI for multiple apps - Android Developer
Layout Inspector - Android Developer
TextView#textIsSelectable - Android Developer
UiAutomatorについて - Qiita
寿限無 - Wiipedia

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした