Это седьмая часть серии «Android Full Tutorial» . Полное приложение предназначено, чтобы обеспечить легкий способ выполнять поиск фильмов / актеров через Интернет. В первой части серии ( «Основной интерфейс действий» ) мы создали проект Eclipse и настроили базовый интерфейс для основной деятельности приложения. Во второй части ( «Использование HTTP API» ) мы использовали клиентскую библиотеку Apache HTTP для использования внешнего HTTP API и интеграции возможностей поиска API в наше приложение. В третьей части ( «Анализ XML-ответа» ) мы увидели, как анализировать XML-ответ, используя встроенные в Android возможности синтаксического анализа XML. В четвертой части ( «Выполнение запроса API асинхронно из основного действия» ) мы связали вместе службы HTTP-ретривера и XML-парсера, чтобы выполнить запрос поиска API из основного действия нашего приложения. Запрос был выполнен асинхронно в фоновом потоке, чтобы избежать блокировки основного потока пользовательского интерфейса. В пятой части ( «Запуск новых действий с намерениями» ) мы увидели, как запустить новое действие и как перенести данные из одного действия в другое. В шестой части ( «Настроенное представление списка для представления данных» ) мы создали настраиваемое представление списка, чтобы обеспечить лучшее визуальное представление данных. В этой части мы собираемся создать меню параметров и настраиваемые диалоги, чтобы облегчить взаимодействие с пользователем.
При разработке приложений для мобильных устройств, которые довольно ограничены с точки зрения ресурсов (процессора, памяти или размера экрана), мы должны стремиться максимально использовать имеющуюся площадь экрана. В Android одним из способов в полной мере использовать размеры экрана является использование меню параметров, когда доступные параметры должны быть представлены пользователю. Как вы можете видеть на следующем рисунке, при обычных операциях параметры не видны пользователю, и только после того, как он их попросит, они появятся. Когда это происходит, меню помещается на передний план, что означает, что оно «скрывает» любой другой элемент пользовательского интерфейса, который существует под ним.
Одним из особых аспектов создания и использования меню Android является то, что они предоставляют пользователю знакомый интерфейс для доступа к функциям и настройкам приложения. Другими словами, не изобретайте колесо, просто используйте встроенную функциональность, когда вы хотите создать меню. Официальная страница документации по Android содержит подробное руководство по созданию меню . В этом уроке мы увидим самый быстрый и простой способ создания меню и наполнения их соответствующими параметрами.
Неудивительно, что основной класс, вокруг которого строятся меню параметров, называется Menu . На самом деле это интерфейс для управления пунктами меню. По умолчанию каждое действие поддерживает меню параметров действий или параметров. В частности, класс Android Activity предоставляет метод с именем onCreateOptionsMenu, который можно использовать для инициализации содержимого стандартного меню параметров действия. При переопределении этого метода мы обычно используем метод add для добавления новых элементов в меню. Обратите внимание, что метод add и его варианты возвращают объект MenuItem , который является интерфейсом для прямого доступа к ранее созданному пункту меню. Элемент MenuItem может содержать как заголовок, так и значок.
Аргумент itemId метода add позволяет нам выяснить, какие параметры были выбраны. В общем, для обработки таких событий, как выбор пользователем пункта меню, нам необходимо переопределить метод onOptionsItemSelected нашей Activity . Этот метод содержит ссылку на MenuItem, который был выбран, и мы можем использовать метод getItemId , чтобы найти его идентификатор. Таким образом, мы теперь в состоянии узнать, что выбрал пользователь.
В нашем приложении мы будем использовать меню опций, чтобы позволить пользователю либо посетить страницу IMDB определенного фильма, либо просмотреть изображение его плаката в новом окне. Сначала мы определяем идентификаторы доступных элементов, а затем создаем меню, добавляя пункты меню в меню. Обратите внимание, что мы используем заголовок и значок для наших пунктов меню. Наконец, когда выбран вариант, мы проверяем идентификатор пункта меню, чтобы выяснить, какое действие необходимо выполнить. Вот некоторые фрагменты кода для всего вышеперечисленного:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | privatestaticfinalintITEM_VISIT_IMDB = 0;privatestaticfinalintITEM_VIEW_FULL_IMAGE = 1;...@OverridepublicbooleanonCreateOptionsMenu(Menu menu) {    menu.add(Menu.NONE, ITEM_VISIT_IMDB, 0,         getString(R.string.visit_imdb)).setIcon(android.R.drawable.ic_menu_set_as);    menu.add(Menu.NONE, ITEM_VIEW_FULL_IMAGE, 0,         getString(R.string.view_full_image)).setIcon(android.R.drawable.ic_menu_zoom);    returnsuper.onCreateOptionsMenu(menu);}...@OverridepublicbooleanonOptionsItemSelected(MenuItem item) {    switch(item.getItemId()) {        caseITEM_VISIT_IMDB:            visitImdbMoviePage();            returntrue;        caseITEM_VIEW_FULL_IMAGE:            viewFullImagePoster();            returntrue;    }    returnfalse;} | 
Вот изображение того, как будет выглядеть наше меню параметров:
Вторая тема этого руководства — создание и использование диалогов, которые помогают взаимодействовать с пользователями приложения. Самый простой способ сделать это — напрямую использовать класс Dialog . Конструктор Dialog требует в качестве аргумента объект Context , и это будет действие, в рамках которого создается диалог. Как и в случае с Activity , объекты диалога предоставляют метод setContentView , который принимает идентификатор ресурса макета и устанавливает содержимое экрана из этого ресурса макета. Точно так же метод findViewById позволяет нам найти ссылку на представление, которое было идентифицировано атрибутом id из макета XML. Внутренними представлениями можно манипулировать как обычно. В двух словах, это похоже на создание представления для действия .
После того, как мы закончили с созданием диалога, вызов метода show запустит диалог и отобразит его на экране. В этом случае окно размещается на прикладном уровне и непрозрачно. После того, как мы закончили с конкретным диалогом, нам нужно вызвать метод dismiss , чтобы удалить его с экрана. Обратите внимание, что этот метод можно безопасно вызывать из любого потока. И последнее замечание в диалоге : вы можете определить для него заголовок (с помощью метода setTitle ) или указать, что заголовок для него не нужен (с помощью метода requestWindowFeature и функции FEATURE_NO_TITLE ).
В нашем приложении мы собираемся создать два разных типа диалогов: один для показа изображения постера фильма и один для представления обзора фильма. Давайте посмотрим фрагменты кода для тех.
Для диалога изображений постера у нас есть:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 | finalDialog dialog = newDialog(this);dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);dialog.setContentView(R.layout.full_image_layout);...finalButton closeDialogButton = (Button) dialog.findViewById(R.id.close_full_image_dialog_button);imageView = (ImageView) dialog.findViewById(R.id.image_view);                                closeDialogButton.setOnClickListener(newOnClickListener() {                @Override    publicvoidonClick(View v) {        dialog.dismiss();    }});finalImageDownloaderTask task = newImageDownloaderTask();task.execute(imageUrl);        dialog.show(); | 
Обратите внимание, что изображение извлекается асинхронно с помощью ImageDownloaderTask (расширяет класс AsyncTask ), а ImageView заполняется в методе onPostExecute . Для этого диалогового окна макет XML называется «full_image_layout.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 30 31 32 33 34 | <?xmlversion="1.0"encoding="utf-8"?><LinearLayout    android:orientation="vertical"    android:layout_width="fill_parent"    android:layout_height="fill_parent">    <ImageView        android:id="@+id/image_view"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:layout_marginLeft="5dip"        android:layout_marginRight="5dip"        android:layout_marginTop="5dip"        android:layout_marginBottom="5dip"       />              <Button        android:id="@+id/close_full_image_dialog_button"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="@string/close"        android:layout_weight="0.5"        android:layout_marginLeft="10dip"        android:layout_marginRight="10dip"        android:layout_marginTop="5dip"        android:layout_marginBottom="5dip"        android:layout_gravity="center"    />        </LinearLayout> | 
При нажатии на соответствующую кнопку меню, вот что вы собираетесь получить для диалога изображения плаката:
Для диалога обзора фильма у нас есть следующее:
| 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 | finalDialog dialog = newDialog(this);dialog.setContentView(R.layout.movie_overview_dialog);dialog.setTitle(title);finalTextView overviewTextView = (TextView) dialog.findViewById(R.id.movie_overview_text_view);overviewTextView.setText(overview);        finalButton closeButton = (Button) dialog.findViewById(R.id.movie_overview_close_button);        closeButton.setOnClickListener(newOnClickListener() {                @Override    publicvoidonClick(View v) {        dialog.dismiss();    }});        dialog.show(); | 
Соответственно, файл макета XML называется «movie_overview_dialog.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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | <?xmlversion="1.0"encoding="utf-8"?>   android:orientation="vertical"   android:layout_width="fill_parent"   android:layout_height="fill_parent"   >      <ScrollView           android:layout_width="fill_parent"           android:layout_height="fill_parent">                      <LinearLayout               android:orientation="vertical"            android:layout_width="fill_parent"            android:layout_height="fill_parent"            android:paddingLeft="5px"            android:paddingRight="5px"            android:paddingBottom="10px"           >               <TextView                android:id="@+id/movie_overview_text_view"                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:layout_centerHorizontal="true"                android:layout_marginBottom="5dip"                android:layout_marginLeft="5dip"                android:layout_marginRight="5dip"             />                            <Button                android:id="@+id/movie_overview_close_button"                android:text="@string/close"                android:layout_width="wrap_content"                android:layout_height="wrap_content"            />                </LinearLayout>        </ScrollView>    </LinearLayout> | 
Примером диалогового окна обзора фильма является следующее (обратите внимание, что это диалоговое окно появляется, когда пользователь нажимает на один из фильмов в списке):
В целом класс MoviesListActivity теперь выглядит следующим образом:
| 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 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | packagecom.javacodegeeks.android.apps.moviesearchapp;importjava.io.InputStream;importjava.util.ArrayList;importandroid.app.Dialog;importandroid.app.ListActivity;importandroid.app.ProgressDialog;importandroid.content.DialogInterface;importandroid.content.Intent;importandroid.content.DialogInterface.OnCancelListener;importandroid.graphics.Bitmap;importandroid.graphics.BitmapFactory;importandroid.net.Uri;importandroid.os.AsyncTask;importandroid.os.Bundle;importandroid.view.Menu;importandroid.view.MenuItem;importandroid.view.View;importandroid.view.Window;importandroid.view.View.OnClickListener;importandroid.widget.Button;importandroid.widget.ImageView;importandroid.widget.ListView;importandroid.widget.TextView;importandroid.widget.Toast;importcom.javacodegeeks.android.apps.moviesearchapp.io.FlushedInputStream;importcom.javacodegeeks.android.apps.moviesearchapp.model.Movie;importcom.javacodegeeks.android.apps.moviesearchapp.services.HttpRetriever;importcom.javacodegeeks.android.apps.moviesearchapp.ui.MoviesAdapter;importcom.javacodegeeks.android.apps.moviesearchapp.util.Utils;publicclassMoviesListActivity extendsListActivity {            privatestaticfinalintITEM_VISIT_IMDB = 0;    privatestaticfinalintITEM_VIEW_FULL_IMAGE = 1;        privateArrayList<Movie> moviesList = newArrayList<Movie>();    privateMoviesAdapter moviesAdapter;        privateHttpRetriever httpRetriever = newHttpRetriever();        privateProgressDialog progressDialog;    privateImageView imageView;        @SuppressWarnings("unchecked")    @Override    publicvoidonCreate(Bundle savedInstanceState) {                super.onCreate(savedInstanceState);        setContentView(R.layout.movies_layout);        moviesAdapter = newMoviesAdapter(this, R.layout.movie_data_row, moviesList);        moviesList = (ArrayList<Movie>) getIntent().getSerializableExtra("movies");                setListAdapter(moviesAdapter);                if(moviesList!=null&& !moviesList.isEmpty()) {                        moviesAdapter.notifyDataSetChanged();            moviesAdapter.clear();            for(inti = 0; i < moviesList.size(); i++) {                moviesAdapter.add(moviesList.get(i));            }        }                moviesAdapter.notifyDataSetChanged();            }        @Override    publicbooleanonCreateOptionsMenu(Menu menu) {        menu.add(Menu.NONE, ITEM_VISIT_IMDB, 0,                 getString(R.string.visit_imdb)).setIcon(android.R.drawable.ic_menu_set_as);        menu.add(Menu.NONE, ITEM_VIEW_FULL_IMAGE, 0,                 getString(R.string.view_full_image)).setIcon(android.R.drawable.ic_menu_zoom);        returnsuper.onCreateOptionsMenu(menu);    }        @Override    publicbooleanonOptionsItemSelected(MenuItem item) {        switch(item.getItemId()) {            caseITEM_VISIT_IMDB:                visitImdbMoviePage();                returntrue;            caseITEM_VIEW_FULL_IMAGE:                viewFullImagePoster();                returntrue;        }        returnfalse;    }        @Override    protectedvoidonListItemClick(ListView l, View v, intposition, longid) {                super.onListItemClick(l, v, position, id);        finalMovie movie = moviesAdapter.getItem((int)position);        showMovieOverviewDialog(movie.name, movie.overview);            }        privatevoidviewFullImagePoster() {                finalMovie movie = retrieveSelectedMovie();                if(movie==null) {            longToast(getString(R.string.no_movie_selected));            return;        }                String imageUrl = movie.retrieveCoverImage();        if(Utils.isMissing(imageUrl)) {            longToast(getString(R.string.no_imdb_id_found));            return;        }                finalDialog dialog = newDialog(this);        dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);        dialog.setContentView(R.layout.full_image_layout);                finalButton closeDialogButton = (Button) dialog.findViewById(R.id.close_full_image_dialog_button);        imageView = (ImageView) dialog.findViewById(R.id.image_view);                                                closeDialogButton.setOnClickListener(newOnClickListener() {                        @Override            publicvoidonClick(View v) {                dialog.dismiss();            }        });                finalImageDownloaderTask task = newImageDownloaderTask();        task.execute(imageUrl);                dialog.show();                progressDialog = ProgressDialog.show(MoviesListActivity.this,                "Please wait...", "Retrieving data...", true, true);                progressDialog.setOnCancelListener(newOnCancelListener() {                            @Override            publicvoidonCancel(DialogInterface dialog) {                if(task!=null) {                    task.cancel(true);                }            }        });            }        privatevoidvisitImdbMoviePage() {                finalMovie movie = retrieveSelectedMovie();                if(movie==null) {            longToast(getString(R.string.no_movie_selected));            return;        }                String imdbId = movie.imdbId;        if(Utils.isMissing(imdbId)) {            longToast(getString(R.string.no_imdb_id_found));            return;        }                String imdbUrl = IMDB_BASE_URL + movie.imdbId;        Intent imdbIntent = newIntent(Intent.ACTION_VIEW, Uri.parse(imdbUrl));                        startActivity(imdbIntent);            }        privatevoidshowMovieOverviewDialog(finalString title, finalString overview) {                finalDialog dialog = newDialog(this);        dialog.setContentView(R.layout.movie_overview_dialog);        dialog.setTitle(title);        finalTextView overviewTextView = (TextView) dialog.findViewById(R.id.movie_overview_text_view);        overviewTextView.setText(overview);                finalButton closeButton = (Button) dialog.findViewById(R.id.movie_overview_close_button);                closeButton.setOnClickListener(newOnClickListener() {                        @Override            publicvoidonClick(View v) {                dialog.dismiss();            }        });                dialog.show();            }        privateclassImageDownloaderTask extendsAsyncTask<String, Void, Bitmap> {                @Override        protectedBitmap doInBackground(String... params) {            String url = params[0];            InputStream is = httpRetriever.retrieveStream(url);            if(is==null) {                returnnull;            }            returnBitmapFactory.decodeStream(newFlushedInputStream(is));        }                @Override        protectedvoidonPostExecute(finalBitmap result) {                        runOnUiThread(newRunnable() {                @Override                publicvoidrun() {                    if(progressDialog!=null) {                        progressDialog.dismiss();                        progressDialog = null;                    }                    if(result!=null) {                        imageView.setImageBitmap(result);                    }                                    }            });        }            }        privateMovie retrieveSelectedMovie() {        intposition = getSelectedItemPosition();        if(position==-1) {            returnnull;        }        returnmoviesAdapter.getItem((int)position);    }        privatevoidlongToast(CharSequence message) {        Toast.makeText(this, message, Toast.LENGTH_LONG).show();    }    } | 
Это все, ребята. Вы можете найти здесь проект Eclipse, созданный до сих пор. Ура!
- Серия «Android Full Tutorial»
- Android-приложение для преобразования текста в речь
- Обратное геокодирование Android с помощью Yahoo API — PlaceFinder
- Приложение для определения местоположения Android — GPS местоположение
- Установите ОС Android на свой компьютер с VirtualBox
- Охватывая Android-удивительность: краткий обзор



