Статьи

Android: копирование-вставка с намерением и поддержкой HTML

Android обеспечивает поддержку функции копирования и вставки с помощью ClipBoardManager. Сайт developer.android.com содержит краткое описание вместе с изображением, которое помогает понять структуру копирования и вставки.

«Для копирования данных приложение помещает объект ClipData в глобальный буфер обмена ClipboardManager. ClipData содержит один или несколько объектов ClipData.Item и один объект ClipDescription. Чтобы вставить данные, приложение получает ClipData, получает MIME-тип из ClipDescription и получает данные либо из ClipData.Item, либо от поставщика контента, на который ссылается ClipData.Item ».

В этом руководстве рассматривается новейшая функция, представленная в JellyBean, которая поддерживает стилизованный текст. Мы рассмотрим методы, перечисленные ниже.

HTML поддерживаемые методы

  • ClipData.newHtmlText () с буфером обмена
  • Intent.putExtra () с Intent.EXTRA_HTML_TEXT
  • Intent.setClipData () с методом newHtmlText ()

Информация о проекте: метаданные о проекте.
Версия платформы: Android API Level 16.
IDE: Eclipse Helios Service Release 2
Эмулятор: Android 4.1 (API 16)
Обязательное условие: предварительное знание инфраструктуры приложений Android и намерений.

Пример исходного кода

Для начала создайте проект с помощью Eclipse> Файл> Новый проект> Проект приложения Android . Появится следующее диалоговое окно. Заполните обязательные поля, т.е. Имя приложения, Имя проекта и Пакет. Теперь нажмите следующую кнопку.

Когда появится диалоговое окно, выберите BlankActivity и нажмите кнопку « Далее» .

Заполните Имя файла деятельности и Имя файла макета в диалоговом окне, показанном ниже, и нажмите кнопку Готово.

Этот процесс устанавливает основные файлы проекта. Теперь мы собираемся добавить компоненты представления в файл layout_jbclipboard.xml. Вы можете изменить файл макета, используя графический редактор макетов или XML-редактор. В этом макете мы добавим четыре кнопки, а именно: скопировать, вставить, отправить html Intent и отправить намерение clipdata и прикрепить методы onClick с помощью кнопок CopyHtml, pasteHtml, sendHtmlIntent и sendClipdataIntent соответственно. Эти методы будут определены в классе JBClipboard. Мы также включим две кнопки-переключателя: вставка HTML и вставка текста. Эти переключатели помогают выбрать нужный тип текста для извлечения из буфера обмена. У нас также есть три EditView для отображения текста. Первый Editview содержит стилизованный текст, второй показывает либо HTML, либо текстовую строку. Третий показывает строку Coerce HTML. Файл макета показан ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
<LinearLayout xmlns:android='http://schemas.android.com/apk/res/android'
    xmlns:tools='http://schemas.android.com/tools'
    android:layout_width='match_parent'
    android:layout_height='match_parent'
    android:orientation='vertical'>
 
    <EditText
        android:id='@+id/etCopy'
        android:layout_width='fill_parent'
        android:layout_height='wrap_content'
        android:padding='@dimen/padding_medium'
        android:gravity='top'  
        android:scrollHorizontally='false'
        android:inputType='textMultiLine'
         />
    <RadioGroup
       android:id='@+id/rbgTextHTML'
       android:orientation='horizontal'
       android:layout_width='fill_parent'
       android:layout_height='wrap_content'
       >
         <RadioButton
           android:id='@+id/rbHtml'
           android:layout_weight='.5'
           android:layout_width='0dp'
           android:layout_height='wrap_content'
           android:checked='true'
           android:text='@string/rbHtml'/>
       <RadioButton
           android:id='@+id/rbText'
           android:layout_weight='.5'
           android:layout_width='0dp'
           android:layout_height='wrap_content'
           android:text='@string/rbText'/>
      
   </RadioGroup>
    <LinearLayout
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:orientation='horizontal'>
   <Button
       android:layout_width='0dp'
       android:layout_height='wrap_content'
       android:layout_weight='.5'
       android:onClick='copyHtml'
       android:text='@string/btCopy'/>
   <Button
       android:layout_width='0dp'
       android:layout_height='wrap_content'
       android:layout_weight='.5'
       android:onClick='pasteHtml'
       android:text='@string/btPaste'/>
   </LinearLayout>
   <LinearLayout android:layout_width='fill_parent'
       android:layout_height='wrap_content'
       android:orientation='horizontal'>
       <Button
           android:layout_width='0dp'
           android:layout_weight='.5'
           android:layout_height='wrap_content'
           android:onClick='sendHtmlIntent'
           android:text='@string/btSendHtmlIntent'/>
       <Button
           android:layout_width='0dp'
           android:layout_weight='.5'
           android:layout_height='wrap_content'
           android:onClick='sendClipdataIntent'
           android:text='@string/btSendClipdataIntent'/>
   </LinearLayout>
   <TextView
       android:layout_width='fill_parent'
       android:layout_height='wrap_content'
       android:text='@string/tvCopiedText'/>
   <EditText
       android:id='@+id/etPaste'
       android:layout_width='fill_parent'
       android:layout_height='wrap_content'
       android:inputType='textMultiLine'
        android:gravity='top'  
        android:scrollHorizontally='false'
       />
   <TextView
       android:layout_width='fill_parent'
       android:layout_height='wrap_content'
       android:text='@string/tvcoerceText'/>
   <EditText
       android:id='@+id/etPasteCoerceText'
       android:layout_width='fill_parent'
       android:layout_height='wrap_content'
       android:gravity='top'  
       android:scrollHorizontally='false'
       android:inputType='textMultiLine'/>
</LinearLayout>

Теперь нам нужно добавить еще два файла макета для двух разных действий. Давайте определим первый файл макета с помощью Eclipse> File> Android XML file . В конце концов мы получаем следующее диалоговое окно. Убедитесь, что выбран тип ресурса Layout . Назовите файл макета как activity_htmlintent, а затем нажмите « Готово» .

Добавьте два TextViews для отображения текстовых тегов и два EditTexts для отображения HTML и текстовых строк. Этот файл макета будет прикреплен к HTMLIntentActivity.java, который вызывается после трансляции типа «text / html». Содержание activity_htmlintent.xml приведено ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<LinearLayout xmlns:android='http://schemas.android.com/apk/res/android'
    android:layout_width='match_parent'
    android:layout_height='match_parent'
    android:orientation='vertical' >
    <TextView
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:text='@string/tvIntentHtml' />
    <EditText
        android:id='@+id/etHtml'
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:inputType='textMultiLine'
        android:padding='@dimen/padding_medium'
        android:scrollHorizontally='false' />
    <TextView
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:text='@string/tvIntentText' />
    <EditText
        android:id='@+id/etText'
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:inputType='textMultiLine'
        android:padding='@dimen/padding_medium'
        android:scrollHorizontally='false' />
</LinearLayout>

Теперь мы определим еще один файл макета с именем activity_clipdataintent.xml. Выполните аналогичные шаги, приведенные выше, чтобы создать XML-файл макета. Этот файл похож на приведенный выше файл и содержит два EditView и два TextViewThe. Содержание файла приведено ниже. Этот файл макета будет присоединен к классу ClipdataIntentActivity, который вызывается при передаче намерения с объектом Clipdata.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<LinearLayout xmlns:android='http://schemas.android.com/apk/res/android'
    android:layout_width='match_parent'
    android:layout_height='match_parent'
    android:orientation='vertical' >
 
    <TextView
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:text='@string/tvIntentClipdataHtml' />
 
    <EditText
        android:id='@+id/etClipBoardHtml'
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:inputType='textMultiLine'
        android:padding='@dimen/padding_medium'
        android:scrollHorizontally='false' />
 
    <TextView
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:text='@string/tvIntentClipdataText' />
 
    <EditText
        android:id='@+id/etClipBoardText'
        android:layout_width='match_parent'
        android:layout_height='wrap_content'
        android:inputType='textMultiLine'
        android:padding='@dimen/padding_medium'
        android:scrollHorizontally='false' />
</LinearLayout>

Возможно, вы заметили, что в файле макета использованы строковые ресурсы. Определите эту строковую константу в string.xml, как показано ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<resources>
 
    <string name='app_name'>JellyBeanClipboard</string>
    <string name='menu_settings'>Settings</string>
    <string name='title_activity_jbclipboard'>JBClipboard</string>
    <string name='title_activity_htmlintent'>Html Intent Activity</string>
    <string name='title_activity_clipdataintent'>ClipData Intent Activity</string>
    <!-- CDATA tag is required otherwise you can't have the html text
       properly parsed in Textview  -->
   <string name='tvHtml'><![CDATA[<b>Link:</b> <a href='http://www.code4reference.com'>Code4Reference</a>]]></string>
    
   <!-- Text string for button -->
   <string name='btCopy'>Copy</string>
   <string name='btPaste'>Paste</string>
   <string name='btSendHtmlIntent'>send HTML Intent</string>
   <string name='btSendClipdataIntent'>send ClipData Intent</string>
    
   <!-- Text string for RadioButton -->
   <string name='rbHtml'>Paste Html</string>
   <string name='rbText'>Paste Text</string>
    
   <!-- Text string for Text View -->
   <string name='tvCopiedText'><b><i>Copied text</i></b></string>
   <string name='tvcoerceText'><b><i>Copied coerce Text</i></b></string>
    <string name='tvIntentText'><b><i>Intent Text</i></b></string>
   <string name='tvIntentHtml'><b><i>Intent Html</i></b></string>
   <string name='tvIntentClipdataText'><b><i>Intent Clipdata Text</i></b></string>
   <string name='tvIntentClipdataHtml'><b><i>Intent Clipdata Html</i></b></string>
</resources>

Как только мы закончили с файлами макетов, пришло время определить классы действий. Давайте определим основной вид деятельности под названием JBClipboard. У этого действия есть различные методы, которые используют API, поддерживаемые HTML. Встроенные комментарии помогут понять код.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package com.code4reference.rakesh.jellybeanclipboard;
 
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.text.Html;
import android.text.Spannable;
import android.view.View;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.Toast;
 
import com.example.jellybeanclipboard.R;
 
public class JBClipboard extends Activity {
 
 EditText etCopy;
 EditText etPaste;
 EditText etPasteCoerceText;
 RadioButton rbText;
 RadioButton rbHtml;
 ClipboardManager mClipboard;
 
 ClipboardManager.OnPrimaryClipChangedListener mPrimaryChangeListener = new ClipboardManager.OnPrimaryClipChangedListener() {
  /**
   * This method is a callback. It get called when the primary clip
   * on the clipboard changes.
   */
  public void onPrimaryClipChanged() {
   //Toast message will appear whenever the clipboad
   //primary data changes.
   Utility.showToastMessage(getApplicationContext(),
     'Primary clipdata changed', Toast.LENGTH_SHORT);
  }
 };
 
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_jbclipboard);
  etCopy = (EditText) findViewById(R.id.etCopy);
  etPaste = (EditText) findViewById(R.id.etPaste);
  etPasteCoerceText = (EditText) findViewById(R.id.etPasteCoerceText);
  etCopy.setText(Html.fromHtml(getString(R.string.tvHtml)));
  rbText = (RadioButton) findViewById(R.id.rbText);
  rbHtml = (RadioButton) findViewById(R.id.rbHtml);
  mClipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
  mClipboard.addPrimaryClipChangedListener(mPrimaryChangeListener);
 }
 
 /**
  * This method gets called when 'Copy' button get pressed.
  * @param view
  */
 public void copyHtml(View view) {
  String htmlText = getHtmltxt(etCopy);
  String plainText = getOnlyText(etCopy);
  mClipboard.setPrimaryClip(ClipData.newHtmlText('HTML Text', plainText,
    htmlText));
 
 }
 /**
  * This method gets called when 'Paste' button get pressed.
  * @param view
  */
 public void pasteHtml(View view) {
  // Check if there is primary clip exsiting.
  // If it does then echeck the mime type to make sure
  // it has HTML content.
  if (mClipboard.hasPrimaryClip()
    && mClipboard.getPrimaryClipDescription().hasMimeType(
      ClipDescription.MIMETYPE_TEXT_HTML)) {
   // Get the very first item from the clip.
   ClipData.Item item = mClipboard.getPrimaryClip().getItemAt(0);
 
   // If 'Paste HTML' radio button is selected then paste
   // HTML in the Textview.
   if (rbHtml.isChecked()) {
    etPaste.setText(item.getHtmlText());
   } else {
    // Paste the only text version.
    etPaste.setText(item.getText());
   }
   // Paste the CoerceText .
   etPasteCoerceText.setText(item.coerceToText(this));
  }
 }
 /**
  * This method gets called when 'send Html Intent' button get pressed.
  * @param view
  */
 public void sendHtmlIntent(View view) {
  // This kind of intent can be handle by this application
  // Or other application which handle text/html type Intent
  Intent intent = new Intent(Intent.ACTION_SEND);
 
  String htmlText = getHtmltxt(etCopy);
  String text = getOnlyText(etCopy);
  intent.putExtra(Intent.EXTRA_HTML_TEXT, htmlText);
  intent.putExtra(Intent.EXTRA_TEXT, text);
  intent.setType('text/html');
  startActivity(Intent.createChooser(intent, null));
 }
 
 /**
  * This method gets called when 'send Clipdata Intent' button get pressed.
  *
  * @param view
  */
 public void sendClipdataIntent(View view) {
  String htmlText = getHtmltxt(etCopy);
  String plainText = getOnlyText(etCopy);
  Intent intent = new Intent(this, ClipdataIntentActivity.class);
                //create a clipdata object with HTML text.
                //and associate with the intent.
  intent.setClipData(ClipData.newHtmlText(
    'HTML text in Intent's clipdata', plainText, htmlText));
                //Start the activity which can handle clipData object.
  startActivity(intent);
 }
 @Override
    protected void onDestroy() {
        super.onDestroy();
        //Remove the ClipChanged Listener to save the resources.
        mClipboard.removePrimaryClipChangedListener(mPrimaryChangeListener);
    }
 /**
  * This method gets the EditText object and returns the HTML text. It
  * can be called only with EditTexts having spannable object with
  * the HTML text.
  *
  * @param editText
  * @return
  */
 private String getHtmltxt(EditText editText) {
                //get the spannable object from EditText
  Spannable spannable = (Spannable) editText.getText();
                //return the HTML text from spannable object.
  return Html.toHtml(spannable);
 }
 
 /**
  * This method takes the EditText object which has spannable object with HTML
  * text and returns the simple text without HTML tags.
  *
  * @param editText
  * @return
  */
 private String getOnlyText(EditText editText) {
  return editText.getText().toString();
 }
}

Теперь мы собираемся определить HTMLIntentActivity, которая обрабатывает намерения с текстом HTML. Намеренное намерение будет вызвано основной деятельностью (JBClibboard)

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.code4reference.rakesh.jellybeanclipboard;
 
import com.example.jellybeanclipboard.R;
 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.widget.EditText;
 
public class HtmlIntentActivity extends Activity {
 
 private EditText etHtml;
 private EditText etText;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_htmlintent);
  etHtml = (EditText) findViewById(R.id.etHtml);
  etText = (EditText) findViewById(R.id.etText);
 
  //Get the intent that started this activity
  Intent intent = getIntent();
                //Make sure intent and its type is not null.
  if (intent != null && intent.getType() != null
    && intent.getType().equals('text/html')) {
   //This contition will full-fill when this application receive the
   //intent who's type is 'test/html'. In this application sendHtmlIntent
   //method sends this type of Intent.
   Bundle bundle = intent.getExtras();
   if(bundle != null){
    etHtml.setText(bundle.getCharSequence(Intent.EXTRA_HTML_TEXT));
    etText.setText(bundle.getCharSequence(Intent.EXTRA_TEXT));
   }
  }
 }
}

Теперь мы определим другое действие с именем ClipdataIntentActivity, которое обрабатывает Intent, имеющий объект clilpdata. Содержимое класса ClipdataIntentActivity приведено ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.code4reference.rakesh.jellybeanclipboard;
 
import android.app.Activity;
import android.content.ClipboardManager;
import android.content.Intent;
import android.os.Bundle;
import android.widget.EditText;
import android.widget.Toast;
import android.content.ClipData;
import android.content.ClipDescription;
 
import com.example.jellybeanclipboard.R;
 
public class ClipdataIntentActivity extends Activity {
 private EditText etHtml;
 private EditText etText;
 ClipboardManager mClipboard;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_clipdataintent);
  etHtml = (EditText) findViewById(R.id.etClipBoardHtml);
  etText = (EditText) findViewById(R.id.etClipBoardText);
 
  //Get the intent that started this activity
  Intent intent = getIntent();
  if (intent != null) {
   ClipData clipdata = intent.getClipData();
                        //Make sure clipdata object is not null and it has HTML MIME type.
   if (clipdata != null
     && clipdata.getDescription().hasMimeType(
       ClipDescription.MIMETYPE_TEXT_HTML)) {
 
    ClipData.Item item = clipdata.getItemAt(0);
    etHtml.setText(item.getHtmlText());
    etText.setText(item.getText());
   } else {
    Utility.showToastMessage(this,
      'Intent clipdata doesn't have HTML', Toast.LENGTH_SHORT);
   }
 
  }
 }
 
}

Создайте еще один класс с именем Utility. Он содержит метод для отображения тост сообщения. Как вы могли заметить, эта функция является статической, поэтому нет необходимости создавать объект для вызова этого метода.
Всегда полезно помещать служебные методы в отдельный файл и обращаться к нему в разных местах. Таким образом, ваш код будет организован.

01
02
03
04
05
06
07
08
09
10
11
package com.code4reference.rakesh.jellybeanclipboard;
 
import android.content.Context;
import android.widget.Toast;
 
public class Utility {
 
 public static void showToastMessage(Context context, String message, int duration){
  Toast.makeText(context, message, duration).show();
 }
}

И наконец, определите файл манифеста Anroid, который в основном предоставляет информацию о приложении для системы Android. Здесь вы должны заметить, что HtmlIntentActivity Activity имеет фильтр намерений и задает тип Intent как «text / html». В основном это означает, что действие может обрабатывать намерение, тип которого text / html. Оставшаяся часть файла проста и легка для понимания.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    package='com.example.jellybeanclipboard'
    android:versionCode='1'
    android:versionName='1.0' >
 
    <uses-sdk
        android:minSdkVersion='16'
        android:targetSdkVersion='16' />
 
    <application
        android:icon='@drawable/ic_launcher'
        android:label='@string/app_name'
        android:theme='@style/AppTheme' >
        <activity
            android:name='com.code4reference.rakesh.jellybeanclipboard.JBClipboard'
            android:label='@string/title_activity_jbclipboard' >
            <intent-filter>
                <action android:name='android.intent.action.MAIN' />
                <category android:name='android.intent.category.LAUNCHER' />
            </intent-filter>
        </activity>
        <activity android:name='com.code4reference.rakesh.jellybeanclipboard.ClipdataIntentActivity'
            android:label='@string/title_activity_clipdataintent' >
        </activity>
        <activity android:name='com.code4reference.rakesh.jellybeanclipboard.HtmlIntentActivity'
            android:label='@string/title_activity_htmlintent' >
            <intent-filter>
                <action android:name='android.intent.action.SEND' />
                <category android:name='android.intent.category.DEFAULT' />
                <!-- This activity will get launched when proper Intent type will match.
                In this case Intent type is 'text/html' -->
                <data android:mimeType='text/html' />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

Как только вы закончите с кодированием, просто выполните его. Вы можете запустить это приложение на вашем андроид устройстве или эмуляторе. Убедитесь, что версия Android 16 (Jelly Bean) или выше, только тогда это приложение будет работать. Снимки экрана приложений показаны ниже.

Вы можете получить исходный код здесь, на github .

Приятного кодирования и не забудьте поделиться!

Справка: копирование-вставка с намерением и поддержкой HTML на Android. от нашего партнера JCG Ракеша Кусата в блоге Code4Reference .